Skip to content

Commit cef1055

Browse files
committed
feat: improve plugin parameter specification (helps #84)
1 parent 276c9b5 commit cef1055

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+467
-322
lines changed

src/hobbits-core/parameterdelegate.cpp

+57-23
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,25 @@ AbstractParameterEditor* ParameterDelegate::createEditor(QSize targetBounds)
3838
return m_editorCreator(sharedFromThis(), targetBounds);
3939
}
4040

41+
bool ParameterDelegate::jsonTypeCompatible(QJsonValue::Type jsonType, ParameterDelegate::ParameterType type) {
42+
if (jsonType == QJsonValue::Double) {
43+
return (type == ParameterType::Decimal) || (type == ParameterType::Integer);
44+
}
45+
else if (jsonType == QJsonValue::String) {
46+
return type == ParameterType::String;
47+
}
48+
else if (jsonType == QJsonValue::Bool) {
49+
return type == ParameterType::Boolean;
50+
}
51+
else if (jsonType == QJsonValue::Array) {
52+
return type == ParameterType::Array;
53+
}
54+
else if (jsonType == QJsonValue::Object) {
55+
return type == ParameterType::Object;
56+
}
57+
return false;
58+
}
59+
4160
QList<ParameterDelegate::ParameterInfo> ParameterDelegate::parameterInfos() const
4261
{
4362
return m_parameterMap.values();
@@ -48,66 +67,81 @@ ParameterDelegate::ParameterInfo ParameterDelegate::getInfo(QString name) const
4867
return m_parameterMap.value(name);
4968
}
5069

51-
bool ParameterDelegate::validate(const QJsonObject &parameters) const
70+
QStringList ParameterDelegate::validate(const QJsonObject &parameters) const
5271
{
5372
return validateAgainstInfos(parameters, parameterInfos());
5473
}
5574

5675
QString ParameterDelegate::actionDescription(const QJsonObject &parameters) const
5776
{
58-
if (!validate(parameters)) {
77+
if (!validate(parameters).isEmpty()) {
5978
return QString();
6079
}
6180

6281
return m_actionDescriber(parameters);
6382
}
6483

65-
bool ParameterDelegate::validateAgainstInfos(const QJsonObject &parameters, QList<ParameterDelegate::ParameterInfo> infos)
84+
QStringList ParameterDelegate::validateAgainstInfos(const QJsonObject &parameters, QList<ParameterDelegate::ParameterInfo> infos)
6685
{
67-
if (infos.isEmpty()) {
68-
return true;
69-
}
86+
QStringList invalidations;
7087

7188
for (auto param : infos) {
7289
if (!parameters.contains(param.name)) {
7390
if (!param.optional) {
74-
return false;
91+
invalidations.append(QString("Missing required parameter '%1'.").arg(param.name));
92+
continue;
7593
}
7694
}
77-
else if (param.type != parameters.value(param.name).type()) {
78-
return false;
95+
else if (!jsonTypeCompatible(parameters.value(param.name).type(), param.type)) {
96+
invalidations.append(QString("Value of provided parameter '%1' is wrong type.").arg(param.name));
97+
continue;
7998
}
8099

81-
if (param.type == QJsonValue::Array) {
100+
if (param.type == ParameterType::Array) {
82101
QJsonArray array = parameters.value(param.name).toArray();
83102
if (array.isEmpty() && !param.optional) {
84-
return false;
103+
invalidations.append(QString("Required array parameter '%1' is empty.").arg(param.name));
104+
continue;
85105
}
86106
for (QJsonValueRef value: array) {
87107
if (!value.isObject()) {
88-
return false;
108+
invalidations.append(QString("Array parameter '%1' has invalid value '%2'.").arg(param.name).arg(value.toString()));
89109
}
90-
if (!validateAgainstInfos(value.toObject(), param.subInfos)) {
91-
return false;
110+
else {
111+
invalidations.append(validateAgainstInfos(value.toObject(), param.subInfos));
92112
}
93113
}
94114
}
95-
else if (param.type == QJsonValue::Object) {
115+
else if (param.type == ParameterType::Object) {
96116
QJsonValue val = parameters.value(param.name);
97117
if (!val.isObject()) {
98-
return false;
118+
invalidations.append(QString("Object parameter '%1' is not an object.").arg(param.name));
119+
}
120+
else {
121+
invalidations.append(validateAgainstInfos(val.toObject(), param.subInfos));
99122
}
100-
if (!validateAgainstInfos(val.toObject(), param.subInfos)) {
101-
return false;
123+
}
124+
else if (!param.possibleValues.isEmpty()) {
125+
if (!param.possibleValues.contains(parameters.value(param.name))) {
126+
invalidations.append(QString("Parameter '%1' has invalid value '%2'.").arg(param.name).arg(parameters.value(param.name).toString()));
102127
}
103128
}
104-
else if (param.type == QJsonValue::Double && param.hasIntLimits) {
105-
int value = parameters.value(param.name).toInt();
106-
if (value > param.intMax || value < param.intMin) {
107-
return false;
129+
else if (param.type == ParameterType::Integer || param.type == ParameterType::Decimal) {
130+
if (!param.ranges.isEmpty()) {
131+
double value = parameters.value(param.name).toDouble();
132+
bool within = false;
133+
for (auto range : param.ranges) {
134+
if (value >= range.first && value <= range.second) {
135+
within = true;
136+
break;
137+
}
138+
}
139+
if (!within) {
140+
invalidations.append(QString("Parameter '%1' value '%2' is outside valid range.").arg(param.name).arg(value));
141+
}
108142
}
109143
}
110144
}
111145

112-
return true;
146+
return invalidations;
113147
}

src/hobbits-core/parameterdelegate.h

+31-8
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,40 @@
2727
class HOBBITSCORESHARED_EXPORT ParameterDelegate : public QEnableSharedFromThis<ParameterDelegate>
2828
{
2929
public:
30+
31+
/**
32+
* @brief The ParameterType enum provides type classifications for parameters
33+
*
34+
* ParameterType is similar to QJsonValue, but without null or undefined, and with a
35+
* decimal/integer distinction.
36+
*/
37+
enum ParameterType
38+
{
39+
Boolean = 0x1,
40+
Decimal = 0x2,
41+
String = 0x3,
42+
Array = 0x4,
43+
Object = 0x5,
44+
Integer = 0x10
45+
};
46+
47+
/**
48+
* @brief The ParameterInfo struct contains information for a parameter
49+
*
50+
* A parameter's ParameterInfo can be used to provide editors and validation for a parameter.
51+
*/
3052
struct HOBBITSCORESHARED_EXPORT ParameterInfo
3153
{
3254
QString name;
33-
QJsonValue::Type type;
55+
ParameterType type;
3456
bool optional;
3557
QList<ParameterInfo> subInfos;
3658

37-
bool hasIntLimits;
38-
int intMin;
39-
int intMax;
59+
QList<QPair<double, double>> ranges;
60+
QList<QJsonValue> possibleValues;
4061

41-
ParameterInfo(QString name, QJsonValue::Type type, bool optional = true, QList<ParameterInfo> subInfos = {}):
42-
name{name}, type{type}, optional{optional}, subInfos{subInfos}, hasIntLimits(false), intMin(0), intMax(INT_MAX) {}
62+
ParameterInfo(QString name, ParameterType type, bool optional = true, QList<ParameterInfo> subInfos = {}):
63+
name{name}, type{type}, optional{optional}, subInfos{subInfos}, ranges(QList<QPair<double, double>>()), possibleValues(QList<QJsonValue>()) {}
4364

4465
ParameterInfo() = default;
4566
ParameterInfo(const ParameterInfo&) = default;
@@ -56,16 +77,18 @@ class HOBBITSCORESHARED_EXPORT ParameterDelegate : public QEnableSharedFromThis<
5677
std::function<QString(const QJsonObject&)> actionDescriber,
5778
std::function<AbstractParameterEditor*(QSharedPointer<ParameterDelegate>, QSize)> editorCreator);
5879

80+
static bool jsonTypeCompatible(QJsonValue::Type jsonType, ParameterType type);
81+
5982
virtual AbstractParameterEditor* createEditor(QSize targetBounds = QSize());
6083

6184
QList<ParameterInfo> parameterInfos() const;
6285
ParameterInfo getInfo(QString name) const;
6386

64-
bool validate(const QJsonObject &parameters) const;
87+
QStringList validate(const QJsonObject &parameters) const;
6588
QString actionDescription(const QJsonObject &parameters) const;
6689

6790
protected:
68-
static bool validateAgainstInfos(const QJsonObject &parameters, QList<ParameterInfo> infos);
91+
static QStringList validateAgainstInfos(const QJsonObject &parameters, QList<ParameterInfo> infos);
6992
QMap<QString, ParameterInfo> m_parameterMap;
7093
std::function<QString(const QJsonObject&)> m_actionDescriber;
7194
std::function<AbstractParameterEditor*(QSharedPointer<ParameterDelegate>, QSize)> m_editorCreator;

src/hobbits-plugins/analyzers/Find/find.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
Find::Find()
66
{
77
QList<ParameterDelegate::ParameterInfo> infos = {
8-
{"search_string", QJsonValue::String}
8+
{"search_string", ParameterDelegate::ParameterType::String}
99
};
1010

1111
m_delegate = ParameterDelegate::create(
@@ -49,9 +49,11 @@ QSharedPointer<const AnalyzerResult> Find::analyzeBits(
4949
const QJsonObject &parameters,
5050
QSharedPointer<PluginActionProgress> progress)
5151
{
52-
if (!m_delegate->validate(parameters)) {
53-
return AnalyzerResult::error(QString("Invalid parameters passed to %1").arg(name()));
52+
QStringList invalidations = m_delegate->validate(parameters);
53+
if (!invalidations.isEmpty()) {
54+
return AnalyzerResult::error(QString("Invalid parameters passed to %1:\n%2").arg(name()).arg(invalidations.join("\n")));
5455
}
56+
5557
auto findBits = BitArray::fromString(parameters.value("search_string").toString());
5658
auto bits = container->bits();
5759

src/hobbits-plugins/analyzers/Highlight/highlight.cpp

+6-5
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
Highlight::Highlight()
66
{
77
QList<ParameterDelegate::ParameterInfo> infos = {
8-
{"start", QJsonValue::Double},
9-
{"length", QJsonValue::Double},
10-
{"color", QJsonValue::Double, true}
8+
{"start", ParameterDelegate::ParameterType::Integer},
9+
{"length", ParameterDelegate::ParameterType::Integer},
10+
{"color", ParameterDelegate::ParameterType::Integer, true}
1111
};
1212

1313
m_delegate = QSharedPointer<ParameterDelegateUi>(
@@ -55,8 +55,9 @@ QSharedPointer<const AnalyzerResult> Highlight::analyzeBits(
5555
QSharedPointer<PluginActionProgress> progress)
5656
{
5757
progress->setProgressPercent(5);
58-
if (!m_delegate->validate(parameters)) {
59-
return AnalyzerResult::error(QString("Invalid parameters passed to %1").arg(name()));
58+
QStringList invalidations = m_delegate->validate(parameters);
59+
if (!invalidations.isEmpty()) {
60+
return AnalyzerResult::error(QString("Invalid parameters passed to %1:\n%2").arg(name()).arg(invalidations.join("\n")));
6061
}
6162

6263
qint64 start = parameters.value("start").toInt();

src/hobbits-plugins/analyzers/KaitaiStruct/kaitaistruct.cpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
KaitaiStruct::KaitaiStruct()
1414
{
1515
QList<ParameterDelegate::ParameterInfo> infos = {
16-
{PARAM_KSY, QJsonValue::String, true},
17-
{PARAM_PY, QJsonValue::String, true}
16+
{PARAM_KSY, ParameterDelegate::ParameterType::String, true},
17+
{PARAM_PY, ParameterDelegate::ParameterType::String, true}
1818
};
1919

2020
m_delegate = QSharedPointer<ParameterDelegateUi>(
@@ -68,8 +68,9 @@ QSharedPointer<const AnalyzerResult> KaitaiStruct::analyzeBits(
6868
const QJsonObject &parameters,
6969
QSharedPointer<PluginActionProgress> progress)
7070
{
71-
if (!m_delegate->validate(parameters)) {
72-
return AnalyzerResult::error("Invalid parameters given to plugin");
71+
QStringList invalidations = m_delegate->validate(parameters);
72+
if (!invalidations.isEmpty()) {
73+
return AnalyzerResult::error(QString("Invalid parameters passed to %1:\n%2").arg(name()).arg(invalidations.join("\n")));
7374
}
7475

7576
progress->setProgressPercent(2);

src/hobbits-plugins/analyzers/Metadata/metadata.cpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
Metadata::Metadata()
55
{
66
QList<ParameterDelegate::ParameterInfo> infos = {
7-
{"label", QJsonValue::String},
8-
{"contents", QJsonValue::String}
7+
{"label", ParameterDelegate::ParameterType::String},
8+
{"contents", ParameterDelegate::ParameterType::String}
99
};
1010

1111
m_delegate = ParameterDelegate::create(
@@ -51,8 +51,9 @@ QSharedPointer<const AnalyzerResult> Metadata::analyzeBits(
5151
QSharedPointer<PluginActionProgress> progress)
5252
{
5353
Q_UNUSED(progress)
54-
if (!m_delegate->validate(parameters)) {
55-
return AnalyzerResult::error(QString("Invalid parameters passed to %1").arg(name()));
54+
QStringList invalidations = m_delegate->validate(parameters);
55+
if (!invalidations.isEmpty()) {
56+
return AnalyzerResult::error(QString("Invalid parameters passed to %1:\n%2").arg(name()).arg(invalidations.join("\n")));
5657
}
5758

5859
QString label = parameters.value("label").toString();

src/hobbits-plugins/analyzers/WidthFramer/widthframer.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
WidthFramer::WidthFramer()
88
{
99
QList<ParameterDelegate::ParameterInfo> infos = {
10-
{"width", QJsonValue::Double}
10+
{"width", ParameterDelegate::ParameterType::Integer}
1111
};
1212

1313
m_delegate = ParameterDelegate::create(
@@ -53,8 +53,9 @@ QSharedPointer<const AnalyzerResult> WidthFramer::analyzeBits(
5353
const QJsonObject &parameters,
5454
QSharedPointer<PluginActionProgress> progress)
5555
{
56-
if (!m_delegate->validate(parameters)) {
57-
return AnalyzerResult::error("Invalid parameters passed to Width Framer");
56+
QStringList invalidations = m_delegate->validate(parameters);
57+
if (!invalidations.isEmpty()) {
58+
return AnalyzerResult::error(QString("Invalid parameters passed to %1:\n%2").arg(name()).arg(invalidations.join("\n")));
5859
}
5960
progress->setProgressPercent(10);
6061

src/hobbits-plugins/analyzers/WidthFramer/widthframerform.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ QString WidthFramerForm::title()
5252

5353
bool WidthFramerForm::setParameters(QJsonObject parameters)
5454
{
55-
if (!m_delegate->validate(parameters)) {
55+
if (!m_delegate->validate(parameters).isEmpty()) {
5656
return false;
5757
}
5858

src/hobbits-plugins/displays/Ascii/ascii.cpp

+9-8
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ Ascii::Ascii() :
1313

1414

1515
QList<ParameterDelegate::ParameterInfo> infos = {
16-
{"font_size", QJsonValue::Double},
17-
{"column_grouping", QJsonValue::Double},
18-
{"show_headers", QJsonValue::Bool},
19-
{"encoding", QJsonValue::String}
16+
{"font_size", ParameterDelegate::ParameterType::Integer},
17+
{"column_grouping", ParameterDelegate::ParameterType::Integer},
18+
{"show_headers", ParameterDelegate::ParameterType::Boolean},
19+
{"encoding", ParameterDelegate::ParameterType::String}
2020
};
2121

2222
m_delegate = ParameterDelegate::create(
@@ -64,7 +64,7 @@ void Ascii::setDisplayHandle(QSharedPointer<DisplayHandle> displayHandle)
6464
{
6565
m_handle = displayHandle;
6666
DisplayHelper::connectHoverUpdates(this, this, m_handle, [this](QPoint& offset, QSize &symbolSize, int &grouping, int &bitsPerSymbol) {
67-
if (!m_delegate->validate(m_lastParams)) {
67+
if (!m_delegate->validate(m_lastParams).isEmpty()) {
6868
return false;
6969
}
7070
offset = headerOffset(m_lastParams);
@@ -86,8 +86,9 @@ QSharedPointer<DisplayResult> Ascii::renderDisplay(QSize viewportSize, const QJs
8686
{
8787
Q_UNUSED(progress)
8888
m_lastParams = parameters;
89-
if (!m_delegate->validate(parameters)) {
90-
return DisplayResult::error("Invalid parameters");
89+
QStringList invalidations = m_delegate->validate(parameters);
90+
if (!invalidations.isEmpty()) {
91+
return DisplayResult::error(QString("Invalid parameters passed to %1:\n%2").arg(name()).arg(invalidations.join("\n")));
9192
}
9293

9394
QString encoding = parameters.value("encoding").toString();
@@ -105,7 +106,7 @@ QSharedPointer<DisplayResult> Ascii::renderDisplay(QSize viewportSize, const QJs
105106

106107
QSharedPointer<DisplayResult> Ascii::renderOverlay(QSize viewportSize, const QJsonObject &parameters)
107108
{
108-
if (!m_delegate->validate(m_lastParams)) {
109+
if (!m_delegate->validate(m_lastParams).isEmpty()) {
109110
return DisplayResult::nullResult();
110111
}
111112
QSize fontSize = DisplayHelper::textSize(DisplayHelper::monoFont(m_lastParams.value("font_size").toInt()), "0");

0 commit comments

Comments
 (0)