replace pugiconfig.hpp with local header, fix some constness, dry up code

This commit is contained in:
Thomas Fussell 2015-10-13 16:35:22 -04:00
parent 06fcc6f3d0
commit 7316e2184c
11 changed files with 309 additions and 233 deletions

View File

@ -92,6 +92,7 @@ public:
// characteristics
bool garbage_collectible() const;
bool is_date() const;
std::size_t get_xf_index() const;
// position
cell_reference get_reference() const;
@ -110,16 +111,24 @@ public:
style &get_style();
const style &get_style() const;
void set_style(const style &s);
// style shortcuts
std::string get_number_format();
std::string get_number_format() const;
void set_number_format(const std::string &format_code);
std::size_t get_xf_index() const;
font get_font() const;
font &get_font();
const font &get_font() const;
fill &get_fill();
const fill &get_fill() const;
border get_border() const;
alignment get_alignment() const;
protection get_protection() const;
border &get_border();
const border &get_border() const;
alignment &get_alignment();
const alignment &get_alignment() const;
protection &get_protection();
const protection &get_protection() const;
bool pivot_button();
bool pivot_button() const;
bool quote_prefix();
bool quote_prefix() const;
// comment

View File

@ -41,30 +41,34 @@ public:
style copy() const;
font get_font() const;
font &get_font();
const font &get_font() const;
void set_font(font font);
fill &get_fill();
const fill &get_fill() const;
void set_fill(fill &fill);
border get_border() const;
border &get_border();
const border &get_border() const;
void set_border(border borders);
alignment get_alignment() const;
alignment &get_alignment();
const alignment get_alignment() const;
void set_alignment(alignment alignment);
number_format &get_number_format() { return number_format_; }
const number_format &get_number_format() const { return number_format_; }
void set_number_format(number_format number_format);
protection get_protection() const;
protection &get_protection();
const protection &get_protection() const;
void set_protection(protection protection);
bool pivot_button() const;
bool pivot_button();
void set_pivot_button(bool pivot);
bool quote_prefix() const;
bool quote_prefix();
void set_quote_prefix(bool quote);
private:

View File

@ -278,6 +278,7 @@ public:
// extents
row_t get_lowest_row() const;
row_t get_highest_row() const;
row_t get_next_row() const;
column_t get_lowest_column() const;
column_t get_highest_column() const;
range_reference calculate_dimension() const;
@ -297,6 +298,7 @@ public:
std::vector<range_reference> get_merged_ranges() const;
// append
void append();
void append(const std::vector<std::string> &cells);
void append(const std::vector<int> &cells);
void append(const std::vector<date> &cells);

View File

@ -16,102 +16,6 @@
#include "detail/cell_impl.hpp"
#include "detail/comment_impl.hpp"
namespace {
// return s after checking encoding, size, and illegal characters
std::string check_string(std::string s)
{
if (s.size() == 0)
{
return s;
}
// check encoding?
if (s.size() > 32767)
{
s = s.substr(0, 32767); // max string length in Excel
}
for (unsigned char c : s)
{
if (c <= 8 || c == 11 || c == 12 || (c >= 14 && c <= 31))
{
throw xlnt::illegal_character_error(static_cast<char>(c));
}
}
return s;
}
std::pair<bool, long double> cast_numeric(const std::string &s)
{
const char *str = s.c_str();
char *str_end = nullptr;
auto result = std::strtold(str, &str_end);
if (str_end != str + s.size()) return{ false, 0 };
return{ true, result };
}
std::pair<bool, long double> cast_percentage(const std::string &s)
{
if (s.back() == '%')
{
auto number = cast_numeric(s.substr(0, s.size() - 1));
if (number.first)
{
return{ true, number.second / 100 };
}
}
return { false, 0 };
}
std::pair<bool, xlnt::time> cast_time(const std::string &s)
{
xlnt::time result;
try
{
auto last_colon = s.find_last_of(':');
if (last_colon == std::string::npos) return { false, result };
double seconds = std::stod(s.substr(last_colon + 1));
result.second = static_cast<int>(seconds);
result.microsecond = static_cast<int>((seconds - static_cast<double>(result.second)) * 1e6);
auto first_colon = s.find_first_of(':');
if (first_colon == last_colon)
{
auto decimal_pos = s.find('.');
if (decimal_pos != std::string::npos)
{
result.minute = std::stoi(s.substr(0, first_colon));
}
else
{
result.hour = std::stoi(s.substr(0, first_colon));
result.minute = result.second;
result.second = 0;
}
}
else
{
result.hour = std::stoi(s.substr(0, first_colon));
result.minute = std::stoi(s.substr(first_colon + 1, last_colon - first_colon - 1));
}
}
catch (std::invalid_argument)
{
return{ false, result };
}
return { true, result };
}
} // namespace
namespace xlnt {
const xlnt::color xlnt::color::black(0);
@ -278,7 +182,7 @@ void cell::set_value(long double d)
template<>
void cell::set_value(std::string s)
{
set_value_guess_type(s);
d_->set_string(s, get_parent().get_parent().get_guess_types());
}
template<>
@ -286,101 +190,39 @@ void cell::set_value(char const *c)
{
set_value(std::string(c));
}
template<>
void cell::set_value(cell c)
{
}
template<>
void cell::set_value(date d)
{
d_->is_date_ = true;
auto code = xlnt::number_format::format::date_yyyymmdd2;
auto number_format = xlnt::number_format(code);
get_style().set_number_format(number_format);
auto base_date = get_parent().get_parent().get_properties().excel_base_date;
d_->value_numeric_ = d.to_number(base_date);
d_->type_ = type::numeric;
d_->set_date(d.to_number(base_date), xlnt::number_format::format::date_yyyymmdd2);
}
template<>
void cell::set_value(datetime d)
{
d_->is_date_ = true;
auto code = xlnt::number_format::format::date_datetime;
auto number_format = xlnt::number_format(code);
get_style().set_number_format(number_format);
auto base_date = get_parent().get_parent().get_properties().excel_base_date;
d_->value_numeric_ = d.to_number(base_date);
d_->type_ = type::numeric;
d_->set_date(d.to_number(base_date), xlnt::number_format::format::date_datetime);
}
template<>
void cell::set_value(time t)
{
d_->is_date_ = true;
auto code = xlnt::number_format::format::date_time6;
auto number_format = xlnt::number_format(code);
get_style().set_number_format(number_format);
d_->value_numeric_ = t.to_number();
d_->type_ = type::numeric;
d_->set_date(t.to_number(), xlnt::number_format::format::date_time6);
}
template<>
void cell::set_value(timedelta t)
{
auto code = xlnt::number_format::format::date_timedelta;
auto number_format = xlnt::number_format(code);
get_style().set_number_format(number_format);
d_->value_numeric_ = t.to_number();
d_->type_ = type::numeric;
d_->set_date(t.to_number(), xlnt::number_format::format::date_timedelta);
d_->is_date_ = false; // a timedelta isn't actually a date, still uses mostly the same code
}
void cell::set_value_guess_type(const std::string &s)
{
d_->is_date_ = false;
auto temp = check_string(s);
d_->value_string_ = temp;
d_->type_ = type::string;
if (temp.size() > 1 && temp.front() == '=')
{
d_->formula_ = temp;
d_->type_ = type::formula;
d_->value_string_.clear();
}
else if(ErrorCodes.find(s) != ErrorCodes.end())
{
d_->value_string_ = s;
d_->type_ = type::error;
}
else if(get_parent().get_parent().get_guess_types())
{
auto percentage = cast_percentage(s);
if (percentage.first)
{
d_->value_numeric_ = percentage.second;
d_->type_ = type::numeric;
get_style().get_number_format().set_format_code(xlnt::number_format::format::percentage);
}
else
{
auto time = cast_time(s);
if (time.first)
{
set_value(time.second);
}
else
{
auto numeric = cast_numeric(s);
if (numeric.first)
{
set_value(numeric.second);
}
}
}
}
}
bool cell::has_style() const
{
return d_->style_ != nullptr;
@ -428,22 +270,12 @@ bool cell::operator==(const cell &comparand) const
style &cell::get_style()
{
if(d_->style_ == nullptr)
{
d_->style_.reset(new style());
}
return *d_->style_;
return d_->get_style(true);
}
const style &cell::get_style() const
{
if(d_->style_ == nullptr)
{
d_->style_.reset(new style());
}
return *d_->style_;
return d_->get_style();
}
void cell::set_style(const xlnt::style &s)
@ -662,12 +494,17 @@ std::size_t cell::get_xf_index() const
return d_->xf_index_;
}
std::string cell::get_number_format()
{
return get_style().get_number_format().get_format_code_string();
}
std::string cell::get_number_format() const
{
return get_style().get_number_format().get_format_code_string();
}
font cell::get_font() const
font &cell::get_font()
{
return get_style().get_font();
}
@ -682,27 +519,27 @@ const fill &cell::get_fill() const
return get_style().get_fill();
}
border cell::get_border() const
border &cell::get_border()
{
return get_style().get_border();
}
alignment cell::get_alignment() const
alignment &cell::get_alignment()
{
return get_style().get_alignment();
}
protection cell::get_protection() const
protection &cell::get_protection()
{
return get_style().get_protection();
}
bool cell::pivot_button() const
bool cell::pivot_button()
{
return get_style().pivot_button();
}
bool cell::quote_prefix() const
bool cell::quote_prefix()
{
return get_style().quote_prefix();
}

View File

@ -1,12 +1,112 @@
#pragma once
#include <cstdlib>
#include <xlnt/cell/cell.hpp>
#include <xlnt/cell/comment.hpp>
#include <xlnt/common/exceptions.hpp>
#include <xlnt/common/datetime.hpp>
#include <xlnt/common/types.hpp>
#include <xlnt/common/relationship.hpp>
#include "comment_impl.hpp"
namespace {
// return s after checking encoding, size, and illegal characters
std::string check_string(std::string s)
{
if (s.size() == 0)
{
return s;
}
// check encoding?
if (s.size() > 32767)
{
s = s.substr(0, 32767); // max string length in Excel
}
for (unsigned char c : s)
{
if (c <= 8 || c == 11 || c == 12 || (c >= 14 && c <= 31))
{
throw xlnt::illegal_character_error(static_cast<char>(c));
}
}
return s;
}
std::pair<bool, long double> cast_numeric(const std::string &s)
{
const char *str = s.c_str();
char *str_end = nullptr;
auto result = std::strtold(str, &str_end);
if (str_end != str + s.size()) return{ false, 0 };
return{ true, result };
}
std::pair<bool, long double> cast_percentage(const std::string &s)
{
if (s.back() == '%')
{
auto number = cast_numeric(s.substr(0, s.size() - 1));
if (number.first)
{
return{ true, number.second / 100 };
}
}
return { false, 0 };
}
std::pair<bool, xlnt::time> cast_time(const std::string &s)
{
xlnt::time result;
try
{
auto last_colon = s.find_last_of(':');
if (last_colon == std::string::npos) return { false, result };
double seconds = std::stod(s.substr(last_colon + 1));
result.second = static_cast<int>(seconds);
result.microsecond = static_cast<int>((seconds - static_cast<double>(result.second)) * 1e6);
auto first_colon = s.find_first_of(':');
if (first_colon == last_colon)
{
auto decimal_pos = s.find('.');
if (decimal_pos != std::string::npos)
{
result.minute = std::stoi(s.substr(0, first_colon));
}
else
{
result.hour = std::stoi(s.substr(0, first_colon));
result.minute = result.second;
result.second = 0;
}
}
else
{
result.hour = std::stoi(s.substr(0, first_colon));
result.minute = std::stoi(s.substr(first_colon + 1, last_colon - first_colon - 1));
}
}
catch (std::invalid_argument)
{
return{ false, result };
}
return { true, result };
}
} // namespace
namespace xlnt {
class style;
@ -22,7 +122,85 @@ struct cell_impl
cell_impl(worksheet_impl *parent, column_t column, row_t row);
cell_impl(const cell_impl &rhs);
cell_impl &operator=(const cell_impl &rhs);
style &get_style(bool create_if_null)
{
if(style_ == nullptr && create_if_null)
{
style_.reset(new style());
}
return *style_;
}
const style &get_style() const
{
if(style_ == nullptr)
{
throw std::runtime_error("call has_style to check if const cell has a style before accessing it");
}
return *style_;
}
void set_date(long double number, xlnt::number_format::format format_code)
{
is_date_ = true;
auto number_format = xlnt::number_format(format_code);
get_style(true).set_number_format(number_format);
value_numeric_ = number;
type_ = cell::type::numeric;
}
void set_string(const std::string &s, bool guess_types)
{
is_date_ = false;
value_string_ = check_string(s);
type_ = cell::type::string;
if (value_string_.size() > 1 && value_string_.front() == '=')
{
formula_ = value_string_;
type_ = cell::type::formula;
value_string_.clear();
}
else if(cell::ErrorCodes.find(s) != cell::ErrorCodes.end())
{
type_ = cell::type::error;
}
else if(guess_types)
{
auto percentage = cast_percentage(s);
if (percentage.first)
{
value_numeric_ = percentage.second;
type_ = cell::type::numeric;
get_style(true).get_number_format().set_format_code(xlnt::number_format::format::percentage);
}
else
{
auto time = cast_time(s);
if (time.first)
{
set_date(time.second.to_number(), xlnt::number_format::format::date_time6);
}
else
{
auto numeric = cast_numeric(s);
if (numeric.first)
{
value_numeric_ = numeric.second;
type_ = cell::type::numeric;
}
}
}
}
}
cell::type type_;
worksheet_impl *parent_;

View File

@ -0,0 +1,8 @@
#pragma once
/// <summary>
/// Define this here so we don't have to modify pugiconfig.hpp in pugixml source tree.
/// </summary>
#define PUGIXML_HAS_LONG_LONG
#include <pugixml.hpp>

View File

@ -16,22 +16,22 @@ void style::set_number_format(xlnt::number_format format)
number_format_ = format;
}
border style::get_border() const
border &style::get_border()
{
return border_;
}
bool style::pivot_button() const
bool style::pivot_button()
{
return pivot_button_;
}
bool style::quote_prefix() const
bool style::quote_prefix()
{
return quote_prefix_;
}
alignment style::get_alignment() const
alignment &style::get_alignment()
{
return alignment_;
}
@ -46,12 +46,12 @@ const fill &style::get_fill() const
return fill_;
}
font style::get_font() const
font &style::get_font()
{
return font_;
}
protection style::get_protection() const
protection &style::get_protection()
{
return protection_;
}

View File

@ -3,7 +3,6 @@
#include <fstream>
#include <set>
#include <sstream>
#include <pugixml.hpp>
#ifdef _WIN32
#include <Windows.h>
@ -22,10 +21,13 @@
#include <xlnt/writer/style_writer.hpp>
#include "detail/cell_impl.hpp"
#include "detail/include_pugixml.hpp"
#include "detail/workbook_impl.hpp"
#include "detail/worksheet_impl.hpp"
static std::string CreateTemporaryFilename()
namespace {
static std::string create_temporary_filename()
{
#ifdef _WIN32
std::array<TCHAR, MAX_PATH> buffer;
@ -44,6 +46,8 @@ static std::string CreateTemporaryFilename()
return "/tmp/xlsx.xlnt";
#endif
}
} // namespace
namespace xlnt {
namespace detail {
@ -258,30 +262,39 @@ range workbook::get_named_range(const std::string &name)
bool workbook::load(const std::istream &stream)
{
std::string temp_file = CreateTemporaryFilename();
std::string temp_file = create_temporary_filename();
std::ofstream tmp;
tmp.open(temp_file, std::ios::out | std::ios::binary);
tmp << stream.rdbuf();
tmp.close();
load(temp_file);
std::remove(temp_file.c_str());
return true;
}
bool workbook::load(const std::vector<unsigned char> &data)
{
std::string temp_file = CreateTemporaryFilename();
std::string temp_file = create_temporary_filename();
std::ofstream tmp;
tmp.open(temp_file, std::ios::out | std::ios::binary);
for(auto c : data)
{
tmp.put(c);
}
tmp.close();
load(temp_file);
std::remove(temp_file.c_str());
return true;
}
@ -326,12 +339,14 @@ bool workbook::load(const std::string &filename)
auto sheets_node = root_node.child("sheets");
std::vector<std::string> shared_strings;
if(f.has_file("xl/sharedStrings.xml"))
{
shared_strings = xlnt::reader::read_shared_string(f.read("xl/sharedStrings.xml"));
}
std::vector<int> number_format_ids;
if(f.has_file("xl/styles.xml"))
{
pugi::xml_document styles_doc;
@ -526,15 +541,19 @@ void workbook::clear()
bool workbook::save(std::vector<unsigned char> &data)
{
auto temp_file = CreateTemporaryFilename();
auto temp_file = create_temporary_filename();
save(temp_file);
std::ifstream tmp;
tmp.open(temp_file, std::ios::in | std::ios::binary);
auto char_data = std::vector<char>((std::istreambuf_iterator<char>(tmp)),
std::istreambuf_iterator<char>());
data = std::vector<unsigned char>(char_data.begin(), char_data.end());
tmp.close();
std::remove(temp_file.c_str());
return true;
}

View File

@ -412,7 +412,23 @@ void worksheet::unmerge_cells(column_t start_column, row_t start_row, column_t e
unmerge_cells(xlnt::range_reference(start_column, start_row, end_column, end_row));
}
void worksheet::append()
{
get_cell(cell_reference(1, get_next_row()));
}
void worksheet::append(const std::vector<std::string> &cells)
{
xlnt::cell_reference next(1, get_next_row());
for(auto cell : cells)
{
get_cell(next).set_value(cell);
next.set_column_index(next.get_column_index() + 1);
}
}
row_t worksheet::get_next_row() const
{
int row = get_highest_row() + 1;
@ -421,51 +437,46 @@ void worksheet::append(const std::vector<std::string> &cells)
row = 1;
}
int column = 1;
for(auto cell : cells)
{
get_cell(cell_reference(column++, row)).set_value(cell);
}
return row;
}
void worksheet::append(const std::vector<int> &cells)
{
int row = get_highest_row();
int column = 1;
xlnt::cell_reference next(1, get_next_row());
for(auto cell : cells)
{
get_cell(cell_reference(column++, row)).set_value(cell);
get_cell(next).set_value(cell);
next.set_column_index(next.get_column_index() + 1);
}
}
void worksheet::append(const std::vector<date> &cells)
{
int row = get_highest_row();
int column = 1;
xlnt::cell_reference next(1, get_next_row());
for(auto cell : cells)
{
get_cell(cell_reference(column++, row)).set_value(cell);
get_cell(next).set_value(cell);
next.set_column_index(next.get_column_index() + 1);
}
}
void worksheet::append(const std::vector<cell> &cells)
{
int row = get_highest_row();
int column = 1;
xlnt::cell_reference next(1, get_next_row());
for(auto cell : cells)
{
get_cell(cell_reference(column++, row)) = cell;
get_cell(next).set_value(cell);
next.set_column_index(next.get_column_index() + 1);
}
}
void worksheet::append(const std::unordered_map<std::string, std::string> &cells)
{
int row = get_highest_row();
auto row = get_next_row();
for(auto cell : cells)
{
get_cell(cell_reference(cell.first, row)).set_value(cell.second);
@ -474,8 +485,8 @@ void worksheet::append(const std::unordered_map<std::string, std::string> &cells
void worksheet::append(const std::unordered_map<int, std::string> &cells)
{
int row = get_highest_row();
auto row = get_next_row();
for(auto cell : cells)
{
get_cell(cell_reference(cell.first, row)).set_value(cell.second);
@ -484,12 +495,12 @@ void worksheet::append(const std::unordered_map<int, std::string> &cells)
void worksheet::append(const std::vector<int>::const_iterator begin, const std::vector<int>::const_iterator end)
{
int row = get_highest_row();
int column = 1;
xlnt::cell_reference next(1, get_next_row());
for(auto i = begin; i != end; i++)
{
get_cell(cell_reference(column++, row)).set_value(*i);
get_cell(next).set_value(*i);
next.set_column_index(next.get_column_index() + 1);
}
}

View File

@ -374,7 +374,7 @@ public:
ws.get_parent().add_font(font);
auto cell = xlnt::cell(ws, "A1");
TS_ASSERT(cell.get_font() == font);
TS_ASSERT_EQUALS(cell.get_font(), font);
}
void test_fill()

View File

@ -514,7 +514,7 @@ public:
void test_positioning_point()
{
xlnt::worksheet ws(wb_);
TS_ASSERT_EQUALS(ws.get_point_pos(40, 150), "C3");
TS_ASSERT_EQUALS(ws.get_point_pos(150, 40), "C3");
}
void test_positioning_roundtrip()
@ -552,6 +552,10 @@ public:
void test_max_column()
{
xlnt::worksheet ws(wb_);
ws[xlnt::cell_reference("F1")].set_value(10);
ws[xlnt::cell_reference("F2")].set_value(32);
ws[xlnt::cell_reference("F3")].set_formula("=F1+F2");
ws[xlnt::cell_reference("A4")].set_formula("=A1+A2+A3");
TS_ASSERT_EQUALS(ws.get_highest_column(), 6);
}
@ -564,6 +568,10 @@ public:
void test_max_row()
{
xlnt::worksheet ws(wb_);
ws.append();
ws.append(std::vector<int> { 5 });
ws.append();
ws.append(std::vector<int> { 4 });
TS_ASSERT_EQUALS(ws.get_highest_row(), 4);
}