Spreadsheet: Refactored alias checking code, so GUI and Python error messages are aligned.

This commit is contained in:
Eivind Kvedalen 2016-01-27 20:28:24 +01:00 committed by wmayer
parent e5f1e298a6
commit 53dcaccd4e
7 changed files with 126 additions and 28 deletions

View File

@ -176,6 +176,33 @@ const Cell * PropertySheet::getValueFromAlias(const std::string &alias) const
return getValue(it->second); return getValue(it->second);
else else
return 0; return 0;
}
bool PropertySheet::isValidAlias(const std::string &candidate)
{
static const boost::regex gen("^[A-Za-z][_A-Za-z0-9]*$");
boost::cmatch cm;
/* Check if it is used before */
if (getValueFromAlias(candidate) != 0)
return false;
if (boost::regex_match(candidate.c_str(), cm, gen)) {
static const boost::regex e("\\${0,1}([A-Z]{1,2})\\${0,1}([0-9]{1,5})");
if (boost::regex_match(candidate.c_str(), cm, e)) {
const boost::sub_match<const char *> colstr = cm[1];
const boost::sub_match<const char *> rowstr = cm[2];
// A valid cell address?
if (Spreadsheet::validRow(rowstr.str()) >= 0 && Spreadsheet::validColumn(colstr.str()) >= 0)
return false;
}
return true;
}
else
return false;
} }
std::set<CellAddress> PropertySheet::getUsedCells() const std::set<CellAddress> PropertySheet::getUsedCells() const
@ -452,7 +479,15 @@ void PropertySheet::setDisplayUnit(CellAddress address, const std::string &unit)
void PropertySheet::setAlias(CellAddress address, const std::string &alias) void PropertySheet::setAlias(CellAddress address, const std::string &alias)
{ {
if (alias.size() > 0 && !isValidAlias(alias))
throw Base::Exception("Invalid alias");
const Cell * aliasedCell = getValueFromAlias(alias);
Cell * cell = nonNullCellAt(address); Cell * cell = nonNullCellAt(address);
if (aliasedCell != 0 && cell != aliasedCell)
throw Base::Exception("Alias already defined.");
assert(cell != 0); assert(cell != 0);
/* Mark cells depending on this cell dirty; they need to be resolved when an alias changes or disappears */ /* Mark cells depending on this cell dirty; they need to be resolved when an alias changes or disappears */

View File

@ -85,6 +85,8 @@ public:
const Cell * getValueFromAlias(const std::string &alias) const; const Cell * getValueFromAlias(const std::string &alias) const;
bool isValidAlias(const std::string &candidate);
std::set<CellAddress> getUsedCells() const; std::set<CellAddress> getUsedCells() const;
Sheet * sheet() const { return owner; } Sheet * sheet() const { return owner; }

View File

@ -1123,19 +1123,28 @@ void Sheet::setComputedUnit(CellAddress address, const Base::Unit &unit)
} }
/** /**
* @brief Set alias for cell at address \a address to \a alias. * @brief Set alias for cell at address \a address to \a alias. If the alias
* is an empty string, the existing alias is removed.
* @param address Address of cell * @param address Address of cell
* @param alias New alias. * @param alias New alias.
*/ */
void Sheet::setAlias(CellAddress address, const std::string &alias) void Sheet::setAlias(CellAddress address, const std::string &alias)
{ {
const Cell * cell = cells.getValueFromAlias(alias); std::string existingAlias = getAddressFromAlias(alias);
if (cell != 0) if (existingAlias.size() > 0) {
throw Base::Exception("Alias already defined."); if (existingAlias == address.toString()) // Same as old?
else return;
else
throw Base::Exception("Alias already defined");
}
else if (alias.size() == 0) // Empty?
cells.setAlias(address, "");
else if (isValidAlias(alias)) // Valid?
cells.setAlias(address, alias); cells.setAlias(address, alias);
else
throw Base::Exception("Invalid alias");
} }
/** /**
@ -1155,6 +1164,31 @@ std::string Sheet::getAddressFromAlias(const std::string &alias) const
return std::string(); return std::string();
} }
/**
* @brief Determine whether a given alias candiate is valid or not.
*
* A candidate is valid is the string is syntactically correct,
* and the alias does not conflict with an existing property.
*
*/
bool Sheet::isValidAlias(const std::string & candidate)
{
// Valid syntactically?
if (!cells.isValidAlias(candidate))
return false;
// Existing alias? Then it's ok
if (getAddressFromAlias(candidate).size() > 0 )
return true;
// Check to see that is does not crash with any other property in the Sheet object.
if (getPropertyByName(candidate.c_str()))
return false;
else
return true;
}
/** /**
* @brief Set row and column span for the cell at address \a address to \a rows and \a columns. * @brief Set row and column span for the cell at address \a address to \a rows and \a columns.
* @param address Address to upper right corner of cell * @param address Address to upper right corner of cell

View File

@ -142,6 +142,8 @@ public:
std::string getAddressFromAlias(const std::string & alias) const; std::string getAddressFromAlias(const std::string & alias) const;
bool isValidAlias(const std::string &candidate);
void setSpans(CellAddress address, int rows, int columns); void setSpans(CellAddress address, int rows, int columns);
std::set<std::string> dependsOn(CellAddress address) const; std::set<std::string> dependsOn(CellAddress address) const;

View File

@ -84,13 +84,12 @@ std::string Spreadsheet::rowName(int row)
int Spreadsheet::decodeRow(const std::string &rowstr) int Spreadsheet::decodeRow(const std::string &rowstr)
{ {
char * end; int row = validRow(rowstr);
int i = strtol(rowstr.c_str(), &end, 10);
if (i <0 || i >= CellAddress::MAX_ROWS || *end) if (row >= 0)
return row;
else
throw Base::Exception("Invalid row specification."); throw Base::Exception("Invalid row specification.");
return i - 1;
} }
/** /**
@ -103,6 +102,44 @@ int Spreadsheet::decodeRow(const std::string &rowstr)
*/ */
int Spreadsheet::decodeColumn(const std::string &colstr) int Spreadsheet::decodeColumn(const std::string &colstr)
{
int col = validColumn(colstr);
if (col >= 0)
return col;
else
throw Base::Exception("Invalid column specification");
}
/**
* Determine wheter a row specification is valid or not.
*
* @param rowstr Row specified as a string, with "1" being the first row.
*
* @returns 0 or positive on success, -1 on error.
*/
int Spreadsheet::validRow(const std::string &rowstr)
{
char * end;
int i = strtol(rowstr.c_str(), &end, 10);
if (i <0 || i >= CellAddress::MAX_ROWS || *end)
return -1;
return i - 1;
}
/**
* Determine whether a column specification is valid or not.
*
* @param colstr Column specified as a string, with "A" begin the first column.
*
* @returns 0 or positive on success, -1 on error.
*
*/
int Spreadsheet::validColumn(const std::string &colstr)
{ {
int col = 0; int col = 0;
@ -110,7 +147,7 @@ int Spreadsheet::decodeColumn(const std::string &colstr)
if ((colstr[0] >= 'A' && colstr[0] <= 'Z')) if ((colstr[0] >= 'A' && colstr[0] <= 'Z'))
col = colstr[0] - 'A'; col = colstr[0] - 'A';
else else
throw Base::Exception("Invalid column specification"); return -1;
} }
else { else {
col = 0; col = 0;
@ -120,7 +157,7 @@ int Spreadsheet::decodeColumn(const std::string &colstr)
if ((*i >= 'A' && *i <= 'Z')) if ((*i >= 'A' && *i <= 'Z'))
v = *i - 'A'; v = *i - 'A';
else else
throw Base::Exception("Invalid column specification"); return -1;
col = col * 26 + v; col = col * 26 + v;
} }

View File

@ -37,6 +37,9 @@ SpreadsheetExport std::string columnName(int col);
SpreadsheetExport std::string rowName(int row); SpreadsheetExport std::string rowName(int row);
int decodeColumn(const std::string &colstr); int decodeColumn(const std::string &colstr);
int decodeRow(const std::string &rowstr); int decodeRow(const std::string &rowstr);
int validColumn(const std::string &colstr);
int validRow(const std::string &rowstr);
SpreadsheetExport CellAddress stringToAddress(const char *strAddress); SpreadsheetExport CellAddress stringToAddress(const char *strAddress);
SpreadsheetExport void createRectangles(std::set<std::pair<int, int> > & cells, std::map<std::pair<int, int>, std::pair<int, int> > & rectangles); SpreadsheetExport void createRectangles(std::set<std::pair<int, int> > & cells, std::map<std::pair<int, int>, std::pair<int, int> > & rectangles);
SpreadsheetExport std::string quote(const std::string &input); SpreadsheetExport std::string quote(const std::string &input);

View File

@ -204,22 +204,7 @@ void PropertiesDialog::aliasChanged(const QString & text)
{ {
QPalette palette = ui->alias->palette(); QPalette palette = ui->alias->palette();
aliasOk = true; aliasOk = text.isEmpty() || sheet->isValidAlias(Base::Tools::toStdString(text));
if (sheet->getAddressFromAlias(Base::Tools::toStdString(text)).size() > 0)
aliasOk = false;
if (text.indexOf(QRegExp(QString::fromLatin1("^[A-Za-z][_A-Za-z0-9]*$"))) >= 0) {
try {
CellAddress address(text.toUtf8().constData());
aliasOk = false;
}
catch (...) { }
}
else {
if (!text.isEmpty())
aliasOk = false;
}
alias = aliasOk ? Base::Tools::toStdString(text) : ""; alias = aliasOk ? Base::Tools::toStdString(text) : "";
palette.setColor(QPalette::Text, aliasOk ? Qt::black : Qt::red); palette.setColor(QPalette::Text, aliasOk ? Qt::black : Qt::red);