xlnt/source/cell_reference.cpp

206 lines
5.6 KiB
C++
Raw Normal View History

2014-05-21 22:20:30 +08:00
#include <locale>
2014-06-06 04:19:31 +08:00
#include "cell/cell_reference.hpp"
#include "constants.hpp"
#include "common/exceptions.hpp"
#include "worksheet/range_reference.hpp"
2014-05-21 22:20:30 +08:00
namespace xlnt {
std::size_t cell_reference_hash::operator()(const cell_reference &k) const
{
2014-05-22 05:48:51 +08:00
return k.get_row_index() * constants::MaxColumn + k.get_column_index();
2014-05-21 22:20:30 +08:00
}
cell_reference cell_reference::make_absolute(const cell_reference &relative_reference)
{
cell_reference copy = relative_reference;
copy.absolute_ = true;
return copy;
}
cell_reference::cell_reference() : cell_reference(0, 0, false)
{
}
2014-05-21 22:20:30 +08:00
cell_reference::cell_reference(const std::string &string)
{
bool absolute = false;
auto split = split_reference(string, absolute, absolute);
*this = cell_reference(split.first, split.second, absolute);
}
cell_reference::cell_reference(const char *reference_string) : cell_reference(std::string(reference_string))
{
}
2014-05-22 05:48:51 +08:00
cell_reference::cell_reference(const std::string &column, row_t row, bool absolute)
2014-05-21 22:20:30 +08:00
: column_index_(column_index_from_string(column) - 1),
row_index_(row - 1),
absolute_(absolute)
{
2014-05-22 06:13:32 +08:00
if(row == 0 || row_index_ >= constants::MaxRow || column_index_ >= constants::MaxColumn)
2014-05-21 22:20:30 +08:00
{
2014-05-31 06:42:25 +08:00
throw cell_coordinates_exception(column_index_, row_index_);
2014-05-21 22:20:30 +08:00
}
}
2014-05-22 05:48:51 +08:00
cell_reference::cell_reference(column_t column_index, row_t row_index, bool absolute)
2014-05-21 22:20:30 +08:00
: column_index_(column_index),
row_index_(row_index),
absolute_(absolute)
{
2014-05-22 06:13:32 +08:00
if(row_index_ >= constants::MaxRow || column_index_ >= constants::MaxColumn)
2014-05-21 22:20:30 +08:00
{
2014-05-31 06:42:25 +08:00
throw cell_coordinates_exception(column_index_, row_index_);
2014-05-21 22:20:30 +08:00
}
}
std::string cell_reference::to_string() const
{
if(absolute_)
{
return std::string("$") + column_string_from_index(column_index_ + 1) + "$" + std::to_string(row_index_ + 1);
}
return column_string_from_index(column_index_ + 1) + std::to_string(row_index_ + 1);
}
2014-05-22 05:48:51 +08:00
range_reference cell_reference::to_range() const
{
return range_reference(column_index_, row_index_, column_index_, row_index_);
}
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;
// Convert a coordinate string like 'B12' to a tuple ('B', 12)
bool column_part = true;
std::string column_string;
for(auto character : reference_string)
{
char upper = std::toupper(character, std::locale::classic());
if(std::isalpha(character, std::locale::classic()))
{
if(column_part)
{
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
}
}
else
{
if(column_part)
{
column_part = false;
}
else if(!(std::isdigit(character, std::locale::classic()) || character == '$'))
{
2014-05-31 06:42:25 +08:00
throw cell_coordinates_exception(reference_string);
2014-05-21 22:20:30 +08:00
}
}
}
std::string row_string = reference_string.substr(column_string.length());
2014-06-06 05:42:15 +08:00
if(row_string.length() == 0)
{
throw cell_coordinates_exception(reference_string);
}
2014-05-21 22:20:30 +08:00
if(column_string[0] == '$')
{
absolute_row = true;
column_string = column_string.substr(1);
}
if(row_string[0] == '$')
{
absolute_column = true;
row_string = row_string.substr(1);
}
return {column_string, std::stoi(row_string)};
}
cell_reference cell_reference::make_offset(int column_offset, int row_offset) const
{
return cell_reference(column_index_ + column_offset, row_index_ + row_offset);
}
bool cell_reference::operator==(const cell_reference &comparand) const
{
return comparand.column_index_ == column_index_
&& comparand.row_index_ == row_index_
&& absolute_ == comparand.absolute_;
}
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
{
if(column_string.length() > 3 || column_string.empty())
{
2014-06-06 05:42:15 +08:00
throw column_string_index_exception();
2014-05-21 22:20:30 +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;
for(int i = static_cast<int>(column_string.length()) - 1; i >= 0; i--)
{
if(!std::isalpha(column_string[i], std::locale::classic()))
{
2014-06-06 05:42:15 +08:00
throw column_string_index_exception();
2014-05-21 22:20:30 +08:00
}
column_index += (std::toupper(column_string[i], std::locale::classic()) - 'A' + 1) * place;
place *= 26;
}
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
2014-05-22 05:48:51 +08:00
if(column_index < 1 || column_index > constants::MaxColumn)
2014-05-21 22:20:30 +08:00
{
2014-06-06 05:42:15 +08:00
// auto msg = "Column index out of bounds: " + std::to_string(column_index);
throw column_string_index_exception();
2014-05-21 22:20:30 +08:00
}
auto temp = column_index;
std::string column_letter = "";
while(temp > 0)
{
int quotient = temp / 26, remainder = temp % 26;
// check for exact division and borrow if needed
if(remainder == 0)
{
quotient -= 1;
remainder = 26;
}
column_letter = std::string(1, char(remainder + 64)) + column_letter;
temp = quotient;
}
return column_letter;
}
2014-05-22 06:13:32 +08:00
}