Commit fc5b0951 authored by EXT Arnaud Clère's avatar EXT Arnaud Clère

Precise handling of any/undefined/unknown values

parent 79e211c1
......@@ -27,7 +27,7 @@ detail. Moreover, W1 and RW2 require that we choose a careful compromise between
QTransmogrifier allows binding C++ `data` to a choice of:
* `sequence` of adjacent `data` `item`s
* `record` of named `data` `item`s
* `null` value (meaning no information available on `data`)
* `null` value (meaning information is irrelevant on `data`)
* Atomic values with a textual representation and optional binary ones:
- text (utf16/utf8 encodings)
- boolean (true/false)
......@@ -51,10 +51,11 @@ i((start))--"null()" --> x((end))
i((start))--"bind#lt;T>()"--> x((end))
i((start))--"sequence()" --> QSeq
i((start))--"record()" --> QRec
QSeq --"out()" --> x((end))
QRec --"out()" --> x((end))
QSeq --"item()" --> vs["QVal#lt;QSeq>"]
QRec --"item(name)" --> vr["QVal#lt;QRec>"]
QSeq --"out()" --> x((end))
QRec --"out()" --> x((end))
QSeq --"item()" --> vs["QVal#lt;QSeq>"]
QRec --"item(name)" --> vr["QVal#lt;QRec>"]
i((start))--"any()" --> x((end))
end
```
- Boxes (nested) represent possible states when traversing the data, the automaton is always in a single valid state
......
......@@ -69,7 +69,7 @@ enum SimpleValue : char {
False = 20,
True = 21,
Null = 22,
// Undefined = 23,
Undefined = 23,
// NextByteValue = 24,
// Next16BitFloat = 25,
Next32BitFloat = 26,
......@@ -120,7 +120,8 @@ protected:
bool tryRecord (quint32* cols=nullptr) { levels.push_back(cols);
if (Q_LIKELY(cols)) { putInteger (*cols , cbor::MapType ); }
else { putMajorValue(cbor::IndefiniteLength, cbor::MapType ); } return true; }
bool tryNull ( ) { putSimpleValue(cbor::Null ); return true; }
bool tryAny ( ) { putSimpleValue(cbor::Undefined); return true; }
bool tryNull ( ) { putSimpleValue(cbor::Null ); return true; }
bool tryBind ( QUtf8DataView u) { putInteger (quint64(u.size()), cbor::TextStringType); io->append(u.data(), u.size()); return true; }
bool tryBind ( QString&& s) { QUtf8Data u(s.toUtf8());
putInteger (quint64(u.size()), cbor::TextStringType); io->append(u.utf8()); return true; }
......@@ -189,9 +190,10 @@ public:
/**/ QSequence sequence(quint32* s=nullptr) { return QValueStatus(this).value().sequence(s); }
template<typename T> QValueStatus bind ( T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
protected:
bool trySequence(quint32* =nullptr) { levels.push(Step( )); return true; }
bool tryRecord (quint32* =nullptr) { levels.push(Step(qBindExpectedItem)); return true; }
bool tryNull ( ) { set(QCborValue( )); return true; }
bool trySequence(quint32* =nullptr) { levels.push(Step( )); return true; }
bool tryRecord (quint32* =nullptr) { levels.push(Step(qBindExpectedItem )); return true; }
bool tryAny ( ) { set(QCborValue(QCborValue::Undefined)); return true; }
bool tryNull ( ) { set(QCborValue(nullptr )); return true; }
bool tryBind ( QUtf8DataView u) { set(QCborValue(QString::fromUtf8(u.data()))); return true; }
......@@ -249,6 +251,7 @@ public:
protected:
bool trySequence(quint32* =nullptr) { if (current().isArray ()) { steps.push(Step()); return true; } handleError(qBindExpectedSequence); return false; }
bool tryRecord (quint32* =nullptr) { if (current().isMap ()) { steps.push(Step()); return true; } handleError(qBindExpectedRecord ); return false; }
bool tryAny ( ) { if (current().isUndefined()) { return true; } handleError(qBindUnexpectedValue ); return false; }
bool tryNull ( ) { if (current().isNull ()) { return true; } handleError(qBindExpectedNull ); return false; }
bool tryBind ( QUtf8Data& u) { QString s; if (tryBind(s) ) { u = s .toUtf8 (); return true; } return false; }
bool tryBind ( QString& v) { if (current().isString ()) { v = current().toString (); return true; } handleError(qBindExpectedText ); return false; }
......@@ -294,8 +297,10 @@ protected:
skipTag(); if (isArray () && enterContainer()) { levels.push(Level()); return true; } handleError(qBindExpectedSequence); return false; }
bool tryRecord (quint32* s=nullptr) { if (caching) { cacheLevel++; return caching->tryRecord(s); }
skipTag(); if (isMap () && enterContainer()) { levels.push(Level()); return true; } handleError(qBindExpectedRecord ); return false; }
bool tryAny ( ) { if (caching) { return caching->tryAny() && cacheOut(); }
skipTag(); return next(); }
bool tryNull ( ) { if (caching) { return caching->tryNull() && cacheOut(); }
skipTag(); if ((isNull() || isUndefined()) && next()) { return true; } handleError(qBindExpectedNull); return false; }
skipTag(); if (isNull() && next()) { return true; } handleError(qBindExpectedNull); return false; }
bool tryBind ( QUtf8Data& u) { QString s; if (tryBind(s)) { u = s.toUtf8(); return true; } return false; }
bool tryBind ( QString& s) { if (caching) { return caching->tryBind(s) && cacheOut(); }
skipTag(); if (isString()) {
......@@ -416,7 +421,6 @@ protected:
}
return leaveContainer(); }
bool tryAny() { return next(); }
bool isValid() const noexcept { return const_cast<QCborReader*>(this)->lastError()==QCborError::NoError; }
bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, QString("at index:%1 ").arg(currentOffset()).append(context)) : false; }
private:
......@@ -470,10 +474,11 @@ struct QTransmogrifier<QCborValue> {
if (j.isDouble ()) return v.bind(j.toDouble ());
if (j.isString ()) return v.bind(j.toString ());
if (j.isByteArray()) return v.bind(j.toByteArray());
if (j.isNull() || j.isUndefined() || j.isInvalid() || v->handleError(qBindExpectedValue)) return v.null();
if (j.isNull ()) return v.null();
if (j.isUndefined() || j.isInvalid() || v->handleError(qBindUnexpectedValue)) return v.any();
return QValueStatus();
}
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); }
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
static QValueStatus zap(QValue&& v, QCborValue& j) {
if (v->mode()==Write) {
......@@ -491,12 +496,12 @@ struct QTransmogrifier<QCborValue> {
j = QCborValue(double(u)); v->setErrorHandler(suspended); return r; }
double d; if ((r = v.bind(d))) { j = QCborValue( d ); v->setErrorHandler(suspended); return r; }
QByteArray y; if ((r = v.bind(y))) { j = QCborValue( y ); v->setErrorHandler(suspended); return r; }
r = v.null();
/**/ if ((r = v.null( ))) { j = QCborValue( nullptr ); v->setErrorHandler(suspended); return r; }
v->setErrorHandler(suspended);
if (r || v->handleError(qBindExpectedValue)) { j = QCborValue(); }
if ((r = v.any()) || v->handleError(qBindUnexpectedValue)) { j = QCborValue(); }
return r;
}
else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); }
else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
};
......@@ -511,7 +516,7 @@ struct QTransmogrifier<QCborArray> {
}
return s;
}
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); }
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
static QValueStatus zap(QValue&& v, QCborArray& j) {
if (v->mode()==Write) {
......@@ -527,7 +532,7 @@ struct QTransmogrifier<QCborArray> {
}
return s;
}
else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); }
else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
};
......@@ -546,7 +551,7 @@ struct QTransmogrifier<QCborMap> {
}
return r;
}
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); }
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
static QValueStatus zap(QValue&& v, QCborMap& j) {
if (v->mode()==Write) {
......@@ -562,6 +567,6 @@ struct QTransmogrifier<QCborMap> {
}
return s;
}
else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); }
else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
};
......@@ -70,6 +70,7 @@ protected:
friend class QValueStatus;
bool trySequence(quint32* s=nullptr) { if (s) *io << *s; return true; }
bool tryRecord (quint32* s=nullptr) { if (s) *io << *s; return true; }
bool tryAny ( ) { return handleError(qBindUnexpectedValue); }
bool tryNull ( ) { *io << nullptr; return true; }
bool tryBind ( QUtf8DataView u) { QByteArray ba = QByteArray::fromRawData(u.data(), u.size()+int(sizeof('\0'))); return static_cast<QAbstractValue*>(this)->tryBind(ba); }
......
This diff is collapsed.
......@@ -102,6 +102,8 @@ protected:
}
}
virtual bool tryAny() { return true; }
using QAbstractValue::tryBind; // full overload set so that following overloads calls work (instead of calling themselves and overflowing the call stack)
virtual bool tryBind(const QUtf8Data& r) { QUtf8Data copy(r); return tryBind(std::move(copy)); }
......@@ -389,7 +391,8 @@ protected:
using QAbstractValue::tryBind; // full overload set so that following overloads calls work (instead of calling themselves and overflowing the call stack)
virtual bool tryNull( ) { return hidden() ? true : I<=d ? itemBind()->tryNull() : write(QVariant()); }
virtual bool tryAny ( ) { return hidden() ? true : I<=d ? itemBind()->tryAny() : write(QVariant()); }
virtual bool tryNull( ) { return hidden() ? true : I<=d ? itemBind()->tryNull() : write(QVariant::fromValue(nullptr)); }
virtual bool tryBind( QString&& u) { return hidden() ? true : I<=d ? itemBind()->tryBind(std::move(u)) : write(u ); }
virtual bool tryBind( bool&& b) { return hidden() ? true : I<=d ? itemBind()->tryBind(std::move(b)) : write(b ); }
virtual bool tryBind( float&& f) { return hidden() ? true : I<=d ? itemBind()->tryBind(std::move(f)) : write(f ); }
......@@ -595,7 +598,8 @@ protected:
using QAbstractValue::tryBind; // full overload set so that following overloads calls work (instead of calling themselves and overflowing the call stack)
virtual bool tryNull( ) { return hidden() ? true : I<=d ? itemBind()->tryNull() : read().isNull () || !read().isValid(); }
virtual bool tryAny ( ) { return hidden() ? true : I<=d ? itemBind()->tryAny() : !read().isValid () ; }
virtual bool tryNull( ) { return hidden() ? true : I<=d ? itemBind()->tryNull() : read().isNull () ; }
virtual bool tryBind( QString&& u) { return hidden() ? true : I<=d ? itemBind()->tryBind(std::move(u)) : u==read().toString() ; }
virtual bool tryBind( bool&& b) { return hidden() ? true : I<=d ? itemBind()->tryBind(std::move(b)) : b==read().toBool () ; }
virtual bool tryBind( float&& f) { return hidden() ? true : I<=d ? itemBind()->tryBind(std::move(f)) : qFuzzyCompare(f,read().toFloat ()); }
......
......@@ -65,7 +65,9 @@ protected:
bool tryBind (QUtf8DataView u) { settings->setValue(key(), QString::fromUtf8(u.data(), u.size())); return true; }
bool tryBind ( QStringView s) { settings->setValue(key(), s.toString() ); return true; }
bool tryBind ( QString&& s) { settings->setValue(key(), s ); return true; }
bool tryNull ( ) { settings->setValue(key(), QVariant() ); return true; }
bool tryAny () { settings->setValue(key(), QVariant() ); return true; }
bool tryNull() { settings->setValue(key(), QVariant::fromValue(nullptr)); return true; }
bool trySequence(quint32* =nullptr) { settings->beginGroup(key()); levels.push(Level( )); return true; }
bool tryRecord (quint32* =nullptr) { settings->beginGroup(key()); levels.push(Level(qBindExpectedItem)); return true; }
......@@ -129,7 +131,9 @@ protected:
bool trySequence(quint32* =nullptr) { if (levels.last().isGroup) { levels.push(Level( )); return true; } handleError(qBindExpectedSequence); return false; }
bool tryRecord (quint32* =nullptr) { if (levels.last().isGroup) { levels.push(Level(qBindExpectedItem)); return true; } handleError(qBindExpectedRecord ); return false; }
bool tryNull ( ) { if (settings->value(key()).isNull()) { return true; } handleError(qBindExpectedNull ); return false; }
bool tryAny () { if (!settings->value(key()).isValid()) { return true; } handleError(qBindUnexpectedValue); return false; }
bool tryNull() { if ( settings->value(key()).isNull ()) { return true; } handleError(qBindExpectedNull ); return false; }
bool tryItem(QIdentifier& k) { levels.last().key=k ; return true; }
bool tryItem( ) { levels.last().idx++ ; return true; }
......
#include "QValue.h"
QIdentifierLiteral qBindExpectedValue ("ExpectedValue" );
QIdentifierLiteral qBindUnexpectedValue ("UnexpectedValue" );
QIdentifierLiteral qBindExpectedItem ("ExpectedItem" );
QIdentifierLiteral qBindExpectedNull ("ExpectedNull" );
QIdentifierLiteral qBindExpectedSequence ("ExpectedSequence" );
......
This diff is collapsed.
......@@ -86,7 +86,8 @@ protected:
bool tryBind( QByteArray&& t) { set(QVariant::fromValue(t)); return true; }
bool tryBind( QVariant&& v) { set( v ); return true; }
bool tryNull( ) { set(QVariant ( )); return true; }
bool tryAny () { set(QVariant ( )); return true; }
bool tryNull() { set(QVariant::fromValue(nullptr)); return true; }
bool trySequence(quint32* =nullptr) { levels.push(Level( )); return true; }
bool tryRecord (quint32* =nullptr) { levels.push(Level(qBindExpectedItem)); return true; }
......@@ -168,7 +169,9 @@ protected:
bool trySequence(quint32* =nullptr) { if (current()->type()==QVariant::List ) { levels.push(Level()); return true; } handleError(qBindExpectedSequence); return false; }
bool tryRecord (quint32* =nullptr) { if (current()->type()==QVariant::Map ) { levels.push(Level()); return true; } handleError(qBindExpectedRecord ); return false; }
bool tryNull ( ) { if (current()->isNull() || !current()->isValid()) { return true; } handleError(qBindExpectedNull ); return false; }
bool tryAny () { if (!current()->isValid()) { return true; } handleError(qBindUnexpectedValue); return false; }
bool tryNull() { if ( current()->isNull ()) { return true; } handleError(qBindExpectedNull ); return false; }
bool tryItem(QIdentifier& k) { levels.last().key=k; return (levels.last().item = current(1)->toMap ().value(QString(levels.last().key.latin1()), QVariant())).isValid(); }
bool tryItem( ) { levels.last().idx++; return (levels.last().item = current(1)->toList().value( levels.last().idx , QVariant())).isValid(); }
......
......@@ -72,9 +72,10 @@ public:
template<typename T> QValueStatus bind(T&& t) { return QValueStatus(this).value().bind(std::forward<T>(t)); }
protected:
bool trySequence(quint32* =nullptr) { io->writeStartElement(tag("sequence").latin1()); att(); return true; }
bool tryRecord (quint32* =nullptr) { io->writeStartElement(tag("record" ).latin1()); att(); return true; }
bool tryNull ( ) { io->writeEmptyElement(tag("null" ).latin1()); att(); return true; }
bool trySequence(quint32* =nullptr) { io->writeStartElement(tag("sequence" ).latin1()); att(); return true; }
bool tryRecord (quint32* =nullptr) { io->writeStartElement(tag("record" ).latin1()); att(); return true; }
bool tryAny ( ) { io->writeEmptyElement(tag("undefined").latin1()); att(); return true; }
bool tryNull ( ) { io->writeEmptyElement(tag("null" ).latin1()); att(); return true; }
bool tryBind ( QUtf8DataView u) { writeText("string" ,QString::fromUtf8(u.data(), u.size())); return true; }
bool tryBind ( QStringView s) { writeText("string" , s.toString() ); return true; }
bool tryBind ( QString&& s) { writeText("string" , s ); return true; }
......
......@@ -274,6 +274,7 @@ class MyTextWriter : public QAbstractValueWriter
protected:
bool trySequence(quint32* =nullptr) { ba->append("[") ; return true; }
bool tryRecord (quint32* =nullptr) { ba->append("[") ; return true; }
bool tryAny ( ) { ba->append("*") ; return true; }
bool tryNull ( ) { return true; }
bool tryBind ( const char* u8) { ba->append( u8) ; return true; }
......
......@@ -176,7 +176,7 @@ struct PersonsTable {
ids.clear();
return flatten(persons, v.sequence());
}
else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.null(); }
else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
private:
QStack<int> parentIds;
......@@ -307,6 +307,7 @@ public:
protected:
bool trySequence(quint32* =nullptr) { if (0<level++) utf8->append("["); return true; }
bool tryRecord (quint32* =nullptr) { if (0<level++) utf8->append("["); return true; }
bool tryAny ( ) { utf8->append("*"); return true; }
bool tryNull ( ) { return true; }
bool tryBind ( QUtf8DataView u) { utf8->append(u.data(), u.size()); return true; }
bool tryBind ( float&& n) { static QByteArray s; s.setNum(double(n),'g',6); return tryBind(QUtf8DataView(s.constData(), s.size())); } // with QDebug precision
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment