2014-05-21 22:20:30 +08:00
|
|
|
#include <locale>
|
|
|
|
|
2014-08-14 06:56:34 +08:00
|
|
|
#include <xlnt/cell/cell_reference.hpp>
|
2015-11-03 21:38:09 +08:00
|
|
|
#include <xlnt/utils/exceptions.hpp>
|
2014-08-14 06:56:34 +08:00
|
|
|
#include <xlnt/worksheet/range_reference.hpp>
|
|
|
|
|
2015-11-03 21:38:09 +08:00
|
|
|
#include <detail/constants.hpp>
|
2014-05-21 22:20:30 +08:00
|
|
|
|
|
|
|
namespace xlnt {
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2014-05-21 22:20:30 +08:00
|
|
|
std::size_t cell_reference_hash::operator()(const cell_reference &k) const
|
|
|
|
{
|
2015-10-14 01:56:07 +08:00
|
|
|
return k.get_row() * constants::MaxColumn + k.get_column_index();
|
2014-05-21 22:20:30 +08:00
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2015-11-03 22:51:23 +08:00
|
|
|
cell_reference &cell_reference::make_absolute(bool absolute_column, bool absolute_row)
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
2015-11-03 22:51:23 +08:00
|
|
|
column_absolute(absolute_column);
|
|
|
|
row_absolute(absolute_row);
|
|
|
|
|
|
|
|
return *this;
|
2014-05-21 22:20:30 +08:00
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2015-11-03 22:51:23 +08:00
|
|
|
cell_reference::cell_reference() : cell_reference(1, 1)
|
2014-05-30 08:52:14 +08:00
|
|
|
{
|
|
|
|
}
|
2014-05-21 22:20:30 +08:00
|
|
|
|
|
|
|
cell_reference::cell_reference(const std::string &string)
|
|
|
|
{
|
2015-11-03 22:51:23 +08:00
|
|
|
auto split = split_reference(string, absolute_column_, absolute_row_);
|
|
|
|
|
|
|
|
set_column(split.first);
|
|
|
|
set_row(split.second);
|
2014-05-21 22:20:30 +08:00
|
|
|
}
|
|
|
|
|
2015-11-03 22:51:23 +08:00
|
|
|
cell_reference::cell_reference(const char *reference_string)
|
|
|
|
: cell_reference(std::string(reference_string))
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-11-03 22:51:23 +08:00
|
|
|
cell_reference::cell_reference(const std::string &column, row_t row)
|
|
|
|
: cell_reference(column_index_from_string(column), row)
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-11-03 22:51:23 +08:00
|
|
|
cell_reference::cell_reference(column_t column_index, row_t row)
|
|
|
|
: column_(column_index), row_(row), absolute_column_(false), absolute_row_(false)
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
2015-11-01 22:43:01 +08:00
|
|
|
if (row_ == 0 || row_ >= constants::MaxRow || column_ == 0 || column_ >= constants::MaxColumn)
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
2015-10-14 01:56:07 +08:00
|
|
|
throw cell_coordinates_exception(column_, row_);
|
2014-05-21 22:20:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-01 22:43:01 +08:00
|
|
|
range_reference cell_reference::operator, (const xlnt::cell_reference &other) const
|
2014-07-27 04:19:15 +08:00
|
|
|
{
|
|
|
|
return range_reference(*this, other);
|
|
|
|
}
|
|
|
|
|
2014-05-21 22:20:30 +08:00
|
|
|
std::string cell_reference::to_string() const
|
|
|
|
{
|
2015-11-03 22:51:23 +08:00
|
|
|
std::string string_representation;
|
|
|
|
|
|
|
|
if (absolute_column_)
|
|
|
|
{
|
|
|
|
string_representation.append("$");
|
|
|
|
}
|
|
|
|
|
|
|
|
string_representation.append(column_string_from_index(column_));
|
|
|
|
|
|
|
|
if (absolute_row_)
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
2015-11-03 22:51:23 +08:00
|
|
|
string_representation.append("$");
|
2014-05-21 22:20:30 +08:00
|
|
|
}
|
2015-11-03 22:51:23 +08:00
|
|
|
|
|
|
|
string_representation.append(std::to_string(row_));
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2015-11-03 22:51:23 +08:00
|
|
|
return string_representation;
|
2014-05-21 22:20:30 +08:00
|
|
|
}
|
|
|
|
|
2014-05-22 05:48:51 +08:00
|
|
|
range_reference cell_reference::to_range() const
|
|
|
|
{
|
2015-10-14 01:56:07 +08:00
|
|
|
return range_reference(column_, row_, column_, row_);
|
2014-05-22 05:48:51 +08:00
|
|
|
}
|
|
|
|
|
2015-11-01 22:43:01 +08:00
|
|
|
std::pair<std::string, row_t> cell_reference::split_reference(const std::string &reference_string,
|
|
|
|
bool &absolute_column, bool &absolute_row)
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
|
|
|
absolute_column = false;
|
|
|
|
absolute_row = false;
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2014-05-21 22:20:30 +08:00
|
|
|
// Convert a coordinate string like 'B12' to a tuple ('B', 12)
|
|
|
|
bool column_part = true;
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2014-05-21 22:20:30 +08:00
|
|
|
std::string column_string;
|
2015-11-01 22:43:01 +08:00
|
|
|
|
|
|
|
for (auto character : reference_string)
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
|
|
|
char upper = std::toupper(character, std::locale::classic());
|
2015-11-01 22:43:01 +08:00
|
|
|
|
|
|
|
if (std::isalpha(character, std::locale::classic()))
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
2015-11-01 22:43:01 +08:00
|
|
|
if (column_part)
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
|
|
|
column_string.append(1, upper);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-05-31 06:42:25 +08:00
|
|
|
throw cell_coordinates_exception(reference_string);
|
2014-05-21 22:20:30 +08:00
|
|
|
}
|
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
else if (character == '$')
|
2015-10-14 12:03:48 +08:00
|
|
|
{
|
2015-11-01 22:43:01 +08:00
|
|
|
if (column_part)
|
2015-10-14 12:03:48 +08:00
|
|
|
{
|
2015-11-01 22:43:01 +08:00
|
|
|
if (column_string.empty())
|
2015-10-14 12:03:48 +08:00
|
|
|
{
|
|
|
|
column_string.append(1, upper);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
column_part = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-05-21 22:20:30 +08:00
|
|
|
else
|
|
|
|
{
|
2015-11-01 22:43:01 +08:00
|
|
|
if (column_part)
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
|
|
|
column_part = false;
|
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
else if (!std::isdigit(character, std::locale::classic()))
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
2014-05-31 06:42:25 +08:00
|
|
|
throw cell_coordinates_exception(reference_string);
|
2014-05-21 22:20:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2014-05-21 22:20:30 +08:00
|
|
|
std::string row_string = reference_string.substr(column_string.length());
|
2015-11-01 22:43:01 +08:00
|
|
|
|
|
|
|
if (row_string.length() == 0)
|
2014-06-06 05:42:15 +08:00
|
|
|
{
|
|
|
|
throw cell_coordinates_exception(reference_string);
|
|
|
|
}
|
|
|
|
|
2015-11-01 22:43:01 +08:00
|
|
|
if (column_string[0] == '$')
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
|
|
|
absolute_row = true;
|
|
|
|
column_string = column_string.substr(1);
|
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
|
|
|
|
if (row_string[0] == '$')
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
|
|
|
absolute_column = true;
|
|
|
|
row_string = row_string.substr(1);
|
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
|
|
|
|
return { column_string, std::stoi(row_string) };
|
2014-05-21 22:20:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
cell_reference cell_reference::make_offset(int column_offset, int row_offset) const
|
|
|
|
{
|
2015-11-03 22:51:23 +08:00
|
|
|
//TODO: check for overflow/underflow
|
2015-10-15 06:05:13 +08:00
|
|
|
return cell_reference(static_cast<column_t>(static_cast<int>(column_) + column_offset),
|
2015-11-01 22:43:01 +08:00
|
|
|
static_cast<row_t>(static_cast<int>(row_) + row_offset));
|
2014-05-21 22:20:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool cell_reference::operator==(const cell_reference &comparand) const
|
|
|
|
{
|
2015-11-03 22:51:23 +08:00
|
|
|
return comparand.column_ == column_ &&
|
|
|
|
comparand.row_ == row_ &&
|
|
|
|
absolute_column_ == comparand.absolute_column_ &&
|
|
|
|
absolute_row_ == comparand.absolute_row_;
|
2014-05-21 22:20:30 +08:00
|
|
|
}
|
|
|
|
|
2014-05-22 05:48:51 +08:00
|
|
|
column_t cell_reference::column_index_from_string(const std::string &column_string)
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
2015-11-01 22:43:01 +08:00
|
|
|
if (column_string.length() > 3 || column_string.empty())
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
2014-06-06 05:42:15 +08:00
|
|
|
throw column_string_index_exception();
|
2014-05-21 22:20:30 +08:00
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2014-05-22 05:48:51 +08:00
|
|
|
column_t column_index = 0;
|
2014-05-21 22:20:30 +08:00
|
|
|
int place = 1;
|
2015-11-01 22:43:01 +08:00
|
|
|
|
|
|
|
for (int i = static_cast<int>(column_string.length()) - 1; i >= 0; i--)
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
2015-11-01 22:43:01 +08:00
|
|
|
if (!std::isalpha(column_string[static_cast<std::size_t>(i)], std::locale::classic()))
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
2015-10-15 06:05:13 +08:00
|
|
|
throw column_string_index_exception();
|
2014-05-21 22:20:30 +08:00
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
|
|
|
|
column_index += static_cast<column_t>(
|
|
|
|
(std::toupper(column_string[static_cast<std::size_t>(i)], std::locale::classic()) - 'A' + 1) * place);
|
2014-05-21 22:20:30 +08:00
|
|
|
place *= 26;
|
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2014-05-21 22:20:30 +08:00
|
|
|
return column_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert a column number into a column letter (3 -> 'C')
|
|
|
|
// Right shift the column col_idx by 26 to find column letters in reverse
|
|
|
|
// order.These numbers are 1 - based, and can be converted to ASCII
|
|
|
|
// ordinals by adding 64.
|
2014-05-22 05:48:51 +08:00
|
|
|
std::string cell_reference::column_string_from_index(column_t column_index)
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
|
|
|
// these indicies corrospond to A->ZZZ and include all allowed
|
|
|
|
// columns
|
2015-11-01 22:43:01 +08:00
|
|
|
if (column_index < 1 || column_index > constants::MaxColumn)
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
2015-11-01 22:43:01 +08:00
|
|
|
// auto msg = "Column index out of bounds: " + std::to_string(column_index);
|
2014-06-06 05:42:15 +08:00
|
|
|
throw column_string_index_exception();
|
2014-05-21 22:20:30 +08:00
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2015-10-15 06:05:13 +08:00
|
|
|
int temp = static_cast<int>(column_index);
|
2014-05-21 22:20:30 +08:00
|
|
|
std::string column_letter = "";
|
2015-11-01 22:43:01 +08:00
|
|
|
|
|
|
|
while (temp > 0)
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
|
|
|
int quotient = temp / 26, remainder = temp % 26;
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2014-05-21 22:20:30 +08:00
|
|
|
// check for exact division and borrow if needed
|
2015-11-01 22:43:01 +08:00
|
|
|
if (remainder == 0)
|
2014-05-21 22:20:30 +08:00
|
|
|
{
|
|
|
|
quotient -= 1;
|
|
|
|
remainder = 26;
|
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2014-05-21 22:20:30 +08:00
|
|
|
column_letter = std::string(1, char(remainder + 64)) + column_letter;
|
|
|
|
temp = quotient;
|
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2014-05-21 22:20:30 +08:00
|
|
|
return column_letter;
|
|
|
|
}
|
2014-07-25 05:31:46 +08:00
|
|
|
|
2015-10-14 01:56:07 +08:00
|
|
|
bool cell_reference::operator<(const cell_reference &other)
|
2014-07-25 05:31:46 +08:00
|
|
|
{
|
2015-11-01 22:43:01 +08:00
|
|
|
if (row_ != other.row_)
|
2014-07-25 05:31:46 +08:00
|
|
|
{
|
2015-10-14 01:56:07 +08:00
|
|
|
return row_ < other.row_;
|
2014-07-25 05:31:46 +08:00
|
|
|
}
|
|
|
|
|
2015-10-14 01:56:07 +08:00
|
|
|
return column_ < other.column_;
|
2014-07-25 05:31:46 +08:00
|
|
|
}
|
2015-10-14 01:56:07 +08:00
|
|
|
|
|
|
|
bool cell_reference::operator>(const cell_reference &other)
|
|
|
|
{
|
2015-11-01 22:43:01 +08:00
|
|
|
if (row_ != other.row_)
|
2015-10-14 01:56:07 +08:00
|
|
|
{
|
|
|
|
return row_ > other.row_;
|
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2015-10-14 01:56:07 +08:00
|
|
|
return column_ > other.column_;
|
2014-05-22 06:13:32 +08:00
|
|
|
}
|
2015-10-14 01:56:07 +08:00
|
|
|
|
|
|
|
bool cell_reference::operator<=(const cell_reference &other)
|
|
|
|
{
|
2015-11-01 22:43:01 +08:00
|
|
|
if (row_ != other.row_)
|
2015-10-14 01:56:07 +08:00
|
|
|
{
|
|
|
|
return row_ < other.row_;
|
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2015-10-14 01:56:07 +08:00
|
|
|
return column_ <= other.column_;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cell_reference::operator>=(const cell_reference &other)
|
|
|
|
{
|
2015-11-01 22:43:01 +08:00
|
|
|
if (row_ != other.row_)
|
2015-10-14 01:56:07 +08:00
|
|
|
{
|
|
|
|
return row_ > other.row_;
|
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2015-10-14 01:56:07 +08:00
|
|
|
return column_ >= other.column_;
|
|
|
|
}
|
2015-11-01 22:43:01 +08:00
|
|
|
|
2015-10-14 01:56:07 +08:00
|
|
|
} // namespace xlnt
|