printing: remove objects QVariant map

An artifact from the old grantlee code: the whole parser state
was kept in an untyped QVariant map. One case was particularly
bizarre: the options were a class member and yet added to the
weird map.

Replace this by a strongly typed state structure. Ultimately,
this will allow us to replace the "dive object helper".

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2020-12-12 15:08:56 +01:00 committed by Dirk Hohndel
parent 0cbb448740
commit f42a70586b
2 changed files with 94 additions and 77 deletions

View file

@ -106,11 +106,11 @@ QString TemplateLayout::generate()
QString htmlContent; QString htmlContent;
QVariantList diveList; State state;
struct dive *dive; struct dive *dive;
if (in_planner()) { if (in_planner()) {
diveList.append(QVariant::fromValue(DiveObjectHelperGrantlee(&displayed_dive))); state.dives.append(DiveObjectHelperGrantlee(&displayed_dive));
emit progressUpdated(100.0); emit progressUpdated(100.0);
} else { } else {
int i; int i;
@ -118,7 +118,7 @@ QString TemplateLayout::generate()
//TODO check for exporting selected dives only //TODO check for exporting selected dives only
if (!dive->selected && printOptions.print_selected) if (!dive->selected && printOptions.print_selected)
continue; continue;
diveList.append(QVariant::fromValue(DiveObjectHelperGrantlee(dive))); state.dives.append(DiveObjectHelperGrantlee(dive));
progress++; progress++;
emit progressUpdated(lrint(progress * 100.0 / totalWork)); emit progressUpdated(lrint(progress * 100.0 / totalWork));
} }
@ -126,15 +126,11 @@ QString TemplateLayout::generate()
QString templateContents = readTemplate(printOptions.p_template); QString templateContents = readTemplate(printOptions.p_template);
QHash<QString, QVariant> options;
options["print_options"] = QVariant::fromValue(printOptions);
options["template_options"] = QVariant::fromValue(templateOptions);
options["dives"] = QVariant::fromValue(diveList);
QList<token> tokens = lexer(templateContents); QList<token> tokens = lexer(templateContents);
QString buffer; QString buffer;
QTextStream out(&buffer); QTextStream out(&buffer);
int pos = 0; int pos = 0;
parser(tokens, pos, out, options); parser(tokens, pos, out, state);
htmlContent = out.readAll(); htmlContent = out.readAll();
return htmlContent; return htmlContent;
} }
@ -142,29 +138,26 @@ QString TemplateLayout::generate()
QString TemplateLayout::generateStatistics() QString TemplateLayout::generateStatistics()
{ {
QString htmlContent; QString htmlContent;
QVariantList years;
State state;
int i = 0; int i = 0;
stats_summary_auto_free stats; stats_summary_auto_free stats;
calculate_stats_summary(&stats, false); calculate_stats_summary(&stats, false);
while (stats.stats_yearly != NULL && stats.stats_yearly[i].period) { while (stats.stats_yearly != NULL && stats.stats_yearly[i].period) {
YearInfo year{ &stats.stats_yearly[i] }; YearInfo year{ &stats.stats_yearly[i] };
years.append(QVariant::fromValue(year)); state.years.append(year);
i++; i++;
} }
QString templateFile = QString("statistics") + QDir::separator() + printOptions.p_template; QString templateFile = QString("statistics") + QDir::separator() + printOptions.p_template;
QString templateContents = readTemplate(templateFile); QString templateContents = readTemplate(templateFile);
QHash<QString, QVariant> options;
options["print_options"] = QVariant::fromValue(printOptions);
options["template_options"] = QVariant::fromValue(templateOptions);
options["years"] = QVariant::fromValue(years);
QList<token> tokens = lexer(templateContents); QList<token> tokens = lexer(templateContents);
QString buffer; QString buffer;
QTextStream out(&buffer); QTextStream out(&buffer);
int pos = 0; int pos = 0;
parser(tokens, pos, out, options); parser(tokens, pos, out, state);
htmlContent = out.readAll(); htmlContent = out.readAll();
return htmlContent; return htmlContent;
} }
@ -270,7 +263,7 @@ QList<token> TemplateLayout::lexer(QString input)
static QRegularExpression var(R"(\{\{\s*(\w+)\.(\w+)\s*(\|\s*(\w+))?\s*\}\})"); // Look for {{ stuff.stuff|stuff }} static QRegularExpression var(R"(\{\{\s*(\w+)\.(\w+)\s*(\|\s*(\w+))?\s*\}\})"); // Look for {{ stuff.stuff|stuff }}
QString TemplateLayout::translate(QString s, QHash<QString, QVariant> options) QString TemplateLayout::translate(QString s, State &state)
{ {
QString out; QString out;
int last = 0; int last = 0;
@ -279,8 +272,8 @@ QString TemplateLayout::translate(QString s, QHash<QString, QVariant> options)
QString obname = match.captured(1); QString obname = match.captured(1);
QString memname = match.captured(2); QString memname = match.captured(2);
out += s.mid(last, match.capturedStart() - last); out += s.mid(last, match.capturedStart() - last);
QString listname = options.contains("typeof:" + obname) ? options.value("typeof:" + obname).value<QString>() : obname; QString listname = state.types.value(obname, obname);
QVariant value = getValue(listname, memname, options.value(obname)); QVariant value = getValue(listname, memname, state);
out += value.toString(); out += value.toString();
last = match.capturedEnd(); last = match.capturedEnd();
match = var.match(s, last); match = var.match(s, last);
@ -292,12 +285,31 @@ QString TemplateLayout::translate(QString s, QHash<QString, QVariant> options)
static QRegularExpression forloop(R"(\s*(\w+)\s+in\s+(\w+))"); // Look for "VAR in LISTNAME" static QRegularExpression forloop(R"(\s*(\w+)\s+in\s+(\w+))"); // Look for "VAR in LISTNAME"
static QRegularExpression ifstatement(R"(forloop\.counter\|\s*divisibleby\:\s*(\d+))"); // Look for forloop.counter|divisibleby: NUMBER static QRegularExpression ifstatement(R"(forloop\.counter\|\s*divisibleby\:\s*(\d+))"); // Look for forloop.counter|divisibleby: NUMBER
void TemplateLayout::parser(QList<token> tokenList, int &pos, QTextStream &out, QHash<QString, QVariant> options) template<typename V, typename T>
void TemplateLayout::parser_for(QList<token> tokenList, int &pos, QTextStream &out, State &state,
const V &data, const T *&act)
{
const T *old = act;
int i = 1; // Loop iterators start at one
int olditerator = state.forloopiterator;
int savepos = pos;
for (const T &item: data) {
act = &item;
state.forloopiterator = i;
pos = savepos;
++i;
parser(tokenList, pos, out, state);
}
act = old;
state.forloopiterator = olditerator;
}
void TemplateLayout::parser(QList<token> tokenList, int &pos, QTextStream &out, State &state)
{ {
while (pos < tokenList.length()) { while (pos < tokenList.length()) {
switch (tokenList[pos].type) { switch (tokenList[pos].type) {
case LITERAL: case LITERAL:
out << translate(tokenList[pos].contents, options); out << translate(tokenList[pos].contents, state);
++pos; ++pos;
break; break;
case BLOCKSTART: case BLOCKSTART:
@ -312,32 +324,25 @@ void TemplateLayout::parser(QList<token> tokenList, int &pos, QTextStream &out,
if (match.hasMatch()) { if (match.hasMatch()) {
QString itemname = match.captured(1); QString itemname = match.captured(1);
QString listname = match.captured(2); QString listname = match.captured(2);
options["typeof:" + itemname] = listname; state.types[itemname] = listname;
QString buffer; QString buffer;
QTextStream capture(&buffer); QTextStream capture(&buffer);
QVariantList list = options[listname].value<QVariantList>(); if (listname == "years") {
int savepos = pos; parser_for(tokenList, pos, capture, state, state.years, state.currentYear);
for (int i = 0; i < list.size(); ++i) { } else if (listname == "dives") {
QVariant item = list.at(i); parser_for(tokenList, pos, capture, state, state.dives, state.currentDive);
QVariant olditerator = options["forloopiterator"]; } else if (listname == "cylinders") {
options[itemname] = item; if (state.currentDive)
options["forloopiterator"] = i + 1; parser_for(tokenList, pos, capture, state, state.currentDive->cylinders, state.currentCylinder);
pos = savepos; else
if (listname == "dives") { qWarning("cylinders loop outside of dive");
options["cylinderObjects"] = QVariant::fromValue(item.value<DiveObjectHelperGrantlee>().cylinderObjects); } else if (listname == "cylinderObjects") {
options["cylinders"] = QVariant::fromValue(item.value<DiveObjectHelperGrantlee>().cylinders); if (state.currentDive)
parser_for(tokenList, pos, capture, state, state.currentDive->cylinderObjects, state.currentCylinderObject);
else
qWarning("cylinderObjects loop outside of dive");
} }
parser(tokenList, pos, capture, options); state.types.remove(itemname);
options.remove(itemname);
options.remove("forloopiterator");
if (listname == "dives") {
options.remove("cylinderObjects");
options.remove("cylinders");
}
if (olditerator.isValid())
options["forloopiterator"] = olditerator;
}
options.remove("typeof:" + itemname);
out << capture.readAll(); out << capture.readAll();
} else { } else {
out << "PARSING ERROR: '" << argument << "'"; out << "PARSING ERROR: '" << argument << "'";
@ -353,8 +358,8 @@ void TemplateLayout::parser(QList<token> tokenList, int &pos, QTextStream &out,
int divisor = match.captured(1).toInt(); int divisor = match.captured(1).toInt();
QString buffer; QString buffer;
QTextStream capture(&buffer); QTextStream capture(&buffer);
int counter = options["forloopiterator"].toInt(); int counter = std::max(0, state.forloopiterator);
parser(tokenList, pos, capture, options); parser(tokenList, pos, capture, state);
if (!(counter % divisor)) if (!(counter % divisor))
out << capture.readAll(); out << capture.readAll();
} else { } else {
@ -373,12 +378,11 @@ void TemplateLayout::parser(QList<token> tokenList, int &pos, QTextStream &out,
} }
} }
QVariant TemplateLayout::getValue(QString list, QString property, QVariant option) QVariant TemplateLayout::getValue(QString list, QString property, const State &state)
{ {
if (list == "template_options") { if (list == "template_options") {
template_options object = option.value<template_options>();
if (property == "font") { if (property == "font") {
switch (object.font_index) { switch (templateOptions.font_index) {
case 0: case 0:
return "Arial, Helvetica, sans-serif"; return "Arial, Helvetica, sans-serif";
case 1: case 1:
@ -391,35 +395,36 @@ QVariant TemplateLayout::getValue(QString list, QString property, QVariant optio
return "Verdana, Geneva, sans-serif"; return "Verdana, Geneva, sans-serif";
} }
} else if (property == "borderwidth") { } else if (property == "borderwidth") {
return object.border_width; return templateOptions.border_width;
} else if (property == "font_size") { } else if (property == "font_size") {
return object.font_size / 9.0; return templateOptions.font_size / 9.0;
} else if (property == "line_spacing") { } else if (property == "line_spacing") {
return object.line_spacing; return templateOptions.line_spacing;
} else if (property == "color1") { } else if (property == "color1") {
return object.color_palette.color1.name(); return templateOptions.color_palette.color1.name();
} else if (property == "color2") { } else if (property == "color2") {
return object.color_palette.color2.name(); return templateOptions.color_palette.color2.name();
} else if (property == "color3") { } else if (property == "color3") {
return object.color_palette.color3.name(); return templateOptions.color_palette.color3.name();
} else if (property == "color4") { } else if (property == "color4") {
return object.color_palette.color4.name(); return templateOptions.color_palette.color4.name();
} else if (property == "color5") { } else if (property == "color5") {
return object.color_palette.color5.name(); return templateOptions.color_palette.color5.name();
} else if (property == "color6") { } else if (property == "color6") {
return object.color_palette.color6.name(); return templateOptions.color_palette.color6.name();
} }
} else if (list == "print_options") { } else if (list == "print_options") {
print_options object = option.value<print_options>();
if (property == "grayscale") { if (property == "grayscale") {
if (object.color_selected) { if (printOptions.color_selected) {
return ""; return "";
} else { } else {
return "-webkit-filter: grayscale(100%)"; return "-webkit-filter: grayscale(100%)";
} }
} }
} else if (list =="year") { } else if (list =="years") {
YearInfo object = option.value<YearInfo>(); if (!state.currentYear)
return QVariant();
const YearInfo &object = *state.currentYear;
if (property == "year") { if (property == "year") {
return object.year->period; return object.year->period;
} else if (property == "dives") { } else if (property == "dives") {
@ -451,11 +456,13 @@ QVariant TemplateLayout::getValue(QString list, QString property, QVariant optio
return get_volume_string(object.year->max_sac); return get_volume_string(object.year->max_sac);
} }
} else if (list == "cylinders") { } else if (list == "cylinders") {
if (property == "description") { if (state.currentCylinder && property == "description") {
return option.value<QString>(); return *state.currentCylinder;
} }
} else if (list == "cylinderObjects") { } else if (list == "cylinderObjects") {
CylinderObjectHelper object = option.value<CylinderObjectHelper>(); if (!state.currentCylinderObject)
return QVariant();
const CylinderObjectHelper &object = *state.currentCylinderObject;
if (property == "description") { if (property == "description") {
return object.description; return object.description;
} else if (property == "size") { } else if (property == "size") {
@ -470,7 +477,9 @@ QVariant TemplateLayout::getValue(QString list, QString property, QVariant optio
return object.gasMix; return object.gasMix;
} }
} else if (list == "dives") { } else if (list == "dives") {
DiveObjectHelperGrantlee object = option.value<DiveObjectHelperGrantlee>(); if (!state.currentDive)
return QVariant();
const DiveObjectHelperGrantlee &object = *state.currentDive;
if (property == "number") { if (property == "number") {
return object.number; return object.number;
} else if (property == "id") { } else if (property == "id") {

View file

@ -24,6 +24,10 @@ struct token {
extern QList<QString> grantlee_templates, grantlee_statistics_templates; extern QList<QString> grantlee_templates, grantlee_statistics_templates;
struct YearInfo {
stats_t *year;
};
class TemplateLayout : public QObject { class TemplateLayout : public QObject {
Q_OBJECT Q_OBJECT
public: public:
@ -34,23 +38,27 @@ public:
static void writeTemplate(QString template_name, QString grantlee_template); static void writeTemplate(QString template_name, QString grantlee_template);
private: private:
struct State {
QList<DiveObjectHelperGrantlee> dives;
QList<YearInfo> years;
QMap<QString, QString> types;
int forloopiterator = -1;
const DiveObjectHelperGrantlee *currentDive = nullptr;
const YearInfo *currentYear = nullptr;
const QString *currentCylinder = nullptr;
const CylinderObjectHelper *currentCylinderObject = nullptr;
};
const print_options &printOptions; const print_options &printOptions;
const template_options &templateOptions; const template_options &templateOptions;
QList<token> lexer(QString input); QList<token> lexer(QString input);
void parser(QList<token> tokenList, int &pos, QTextStream &out, QHash<QString, QVariant> options); void parser(QList<token> tokenList, int &pos, QTextStream &out, State &state);
QVariant getValue(QString list, QString property, QVariant option); template<typename V, typename T>
QString translate(QString s, QHash<QString, QVariant> options); void parser_for(QList<token> tokenList, int &pos, QTextStream &out, State &state, const V &data, const T *&act);
QVariant getValue(QString list, QString property, const State &state);
QString translate(QString s, State &state);
signals: signals:
void progressUpdated(int value); void progressUpdated(int value);
}; };
struct YearInfo {
stats_t *year;
};
Q_DECLARE_METATYPE(template_options)
Q_DECLARE_METATYPE(print_options)
Q_DECLARE_METATYPE(YearInfo)
#endif #endif