Vous avez reçu un message "Your GitLab account has been locked ..." ? Pas d'inquiétude : lisez cet article https://docs.gricad-pages.univ-grenoble-alpes.fr/help/unlock/

Commit c6fda812 authored by EXT Arnaud Clère's avatar EXT Arnaud Clère
Browse files

WIP stricter error handling that detected invalid XML chars at slight

performance cost (getting rid of some virtual should improve this)
parent 15204fb8
......@@ -235,7 +235,7 @@ public:
QCborVisitor(QCborValue* v) : cbor(v) { Q_ASSERT(v); }
void reset(QCborValue* v) { cbor=v; Q_ASSERT(v); steps.resize(0); }
QAsciiData currentPath() {
QAsciiData currentPath() const {
QByteArray path;
Q_FOREACH(Step s, steps) {
if (!s.key.isNull()) { path.append('{').append( s.key.utf8() ); }
......@@ -249,10 +249,10 @@ public:
/**/ QSequence sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
template<typename T> QCur bind ( T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
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 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 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 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; }
bool tryBind ( bool& v) { if (current().isBool ()) { v = current().toBool (); return true; } handleError(qBindExpectedBoolean ); return false; }
bool tryBind ( qint64& t) { if (current().isInteger ()) { t = current().toInteger (); return true; } handleError(qBindExpectedInteger ); return false; }
......@@ -264,11 +264,11 @@ protected:
bool tryItem(QIdentifierLiteral u) { steps.last().key=u; return !(steps.last().item = current(1).toMap().value(steps.last().key.latin1())).isUndefined(); }
bool tryItem(QIdentifier& k) { steps.last().key=k; return !(steps.last().item = current(1).toMap().value(steps.last().key.latin1())).isUndefined(); }
bool tryItem( ) { steps.last().idx++; return !(steps.last().item = current(1).toArray(). at(steps.last().idx )).isUndefined(); }
bool tryOut ( ) { steps.pop() ; return true; }
bool tryAny() { return true; }
bool isValid() const noexcept { return cbor; }
bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, QString(currentPath().latin1()).append(context)) : false; }
bool tryOut ( ) { steps.pop() ; return true; }
bool tryAny() { return true; }
bool isValid() const noexcept { return cbor; }
bool handleError(QIdentifierLiteral e, QString context = QString()) const { return errorHandler ? errorHandler(e, QString(currentPath().latin1()).append(context)) : false; }
private:
const QCborValue& current(int outer=0) const { Q_ASSERT(0<=outer); return steps.size()-outer <= 0 ? *cbor : steps[steps.size()-outer-1].item; }
......@@ -289,9 +289,9 @@ public:
QCborReader(QIODevice* io) : QCborStreamReader(io), cacheVisitor(&cachedValue) { Q_ASSERT(io); }
// Shortcuts
/**/ QValue value ( ) { return QCur(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
template<typename T> QCur bind ( T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
/**/ QValue value ( ) { return QCur(this).value(); }
/**/ QSequence sequence(quint32* s=nullptr) { return QCur(this).value().sequence(s); }
template<typename T> QValueEnd bind ( T&& t) { return QCur(this).value().bind(std::forward<T>(t)); }
protected:
bool trySequence(quint32* s=nullptr) { if (caching) { cacheLevel++; return caching->trySequence(s); }
skipTag(); if (isArray () && enterContainer()) { levels.push(Level()); return true; } handleError(qBindExpectedSequence); return false; }
......@@ -414,13 +414,13 @@ protected:
return false;
}
return true; }
bool tryOut ( ) { if (caching) { bool out = caching->tryOut(); if (out) { --cacheLevel; cacheOut(); } return out; }
levels.pop();
while (hasNext()) { tryAny(); }
return leaveContainer(); }
bool tryOut ( ) { if (caching) { bool out = caching->tryOut(); if (out) { --cacheLevel; cacheOut(); } return out; }
levels.pop();
while (hasNext()) { tryAny(); }
return leaveContainer(); }
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; }
bool handleError(QIdentifierLiteral e, QString context = QString()) const { return errorHandler ? errorHandler(e, QString("at %1 ").arg(currentOffset()).append(context)) : false; }
private:
void skipTag() { if (isTag()) next(); }
bool getNumber(double& d) { skipTag();
......@@ -471,20 +471,20 @@ struct QTransmogrifier<QCborValue> {
if (j.isInteger ()) return v.bind(j.toInteger ());
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 ()) return v.null();
return (j.isUndefined() || v->handleError(qBindUnexpectedValue)) ? v.any() : QValueEnd();
}
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
if (j.isByteArray()) return v.bind(j.toByteArray());
if (j.isNull ()) return v.null();
return (j.isUndefined() || v->handleError(qBindUnexpectedValue)) ? v.any() : QValueEnd();
}
else { Q_ASSERT_X(false, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
static QValueEnd zap(QValue&& v, QCborValue& j) {
if (v->mode()==Write) {
return zap(std::move(v),std::move(j));
}
else if (v->mode()==Read) {
QValueEnd r;
auto suspended = v->setErrorHandler();
QCborArray a; if ((r = v.bind(a))) { j = a ; v->setErrorHandler(suspended); return r; }
}
else if (v->mode()==Read) {
QValueEnd r;
auto suspended = v->setErrorHandler();
QCborArray a; if ((r = v.bind(a))) { j = a ; v->setErrorHandler(suspended); return r; }
QCborMap o; if ((r = v.bind(o))) { j = o ; v->setErrorHandler(suspended); return r; }
QString t; if ((r = v.bind(t))) { j = QCborValue( t ); v->setErrorHandler(suspended); return r; }
bool b; if ((r = v.bind(b))) { j = QCborValue( b ); v->setErrorHandler(suspended); return r; }
......@@ -493,11 +493,11 @@ 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; }
/**/ if ((r = v.null( ))) { j = QCborValue( nullptr ); v->setErrorHandler(suspended); return r; }
v->setErrorHandler(suspended);
if ((r = v.any())) { j = QCborValue(); }
return r;
}
/**/ if ((r = v.null( ))) { j = QCborValue( nullptr ); v->setErrorHandler(suspended); return r; }
v->setErrorHandler(suspended);
if ((r = v.any())) { j = QCborValue(); }
return r;
}
else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
}
};
......
......@@ -105,7 +105,7 @@ public:
QJsonVisitor(const QJsonValue* v) : json(v) { Q_ASSERT(v); }
void reset(QJsonValue* v) { json=v; Q_ASSERT(v); steps.resize(0); }
QAsciiData currentPath() {
QAsciiData currentPath() const {
QByteArray path;
Q_FOREACH(Step s, steps) {
if (!s.key.isNull()) { path.append('{').append( s.key.utf8() ); }
......@@ -137,7 +137,7 @@ protected:
bool tryAny() { return true; }
bool isValid() const noexcept { return true; }
bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, QString(currentPath().latin1()).append(context)) : false; }
bool handleError(QIdentifierLiteral e, QString context = QString()) const { return errorHandler ? errorHandler(e, QString(currentPath().latin1()).append(context)) : false; }
private:
const QJsonValue& current(int outer=0) const { Q_ASSERT(0<=outer && json); return steps.size()-outer <= 0 ? *json : steps[steps.size()-outer-1].item; }
......@@ -234,7 +234,7 @@ class QJsonReader : public QAbstractValueReader
public:
QJsonReader(QIODevice* io) : io(io), cacheWriter(&cachedValue), cacheReader(&cachedValue) { Q_ASSERT(io); }
void reset(QIODevice* other) {
errors.clear(); levels.clear();
levels.clear();
line=0; column=0; index=-1;
cachedNumber = None; d=.0; i=0; neg=bool();
cacheLevel = 0; cachedValue = QJsonValue();
......@@ -246,8 +246,6 @@ public:
}
struct Step { int index; const char* end; QMap<QIdentifier,QJsonValue/*TODO QVariant for meta() support*/> cachedItems; Step(int i=-1, const char* e=nullptr) : index(i), end(e) {} };
struct Error { QIdentifierLiteral error; int line; int column; int index; QValueEnd zap(QValue&& value) { QByteArray u(error.utf8()); u.append(' ').append(QByteArray::number(line)).append(':').append(QByteArray::number(column)); return value.bind(QUtf8Data(u)); } };
QVector<Error> errors;
// Shortcuts
/**/ QValue value ( ) { return QCur(this).value(); }
......@@ -387,7 +385,7 @@ protected:
return true; }
bool isValid() const noexcept { return io; }
bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, QString("(%1:%2)").arg(line).arg(column).append(context)) : false; }
bool handleError(QIdentifierLiteral e, QString context = QString()) const { return errorHandler ? errorHandler(e, QString("at %1:%2 ").arg(line).arg(column).append(context)) : false; }
private:
CachedNumber getNumber() {
if (cachedNumber!=None) return cachedNumber;
......@@ -558,7 +556,7 @@ struct QTransmogrifier<QJsonValue> {
double d; if ((r = v.bind(d))) { j = QJsonValue( d ); v->setErrorHandler(suspended); return r; }
/**/ if ((r = v.null( ))) { j = QJsonValue( ); v->setErrorHandler(suspended); return r; }
v->setErrorHandler(suspended);
if (v->handleError(qBindUnexpectedValue)) { j = QJsonValue(QJsonValue::Undefined); }
if ((r = v.any())) { j = QJsonValue(QJsonValue::Undefined); }
return r;
}
else { Q_ASSERT_X(!v, Q_FUNC_INFO, "Unsupported v->mode()"); return v.any(); }
......
......@@ -392,8 +392,6 @@ protected:
// TODO QDate*, QTime
// TODO QPixmap if metadata suggests a QMimeData image ?
virtual bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, context) : true; }
void resetItemData() { ba.clear(); w.reset(&ba); }
QAbstractValue* itemBind() { return static_cast<QAbstractValue*>(&w); }
......@@ -619,18 +617,10 @@ protected:
QAbstractValue* itemBind() { return static_cast<QAbstractValue*>(&r); }
void setItemData(QUtf8Data u) { io.buffer() = u.utf8(); io.seek(0); r.reset(&io); }
bool handleError(QIdentifierLiteral e, QString context = QString()) {
if (errorHandler) {
QString path;
for (auto current = parent; parent.isValid(); current = current.parent()) {
path.prepend(QString('/').append(current.row()).append(',').append(current.column()));
}
return errorHandler(e, path.append(context));
}
else {
return false;
}
QString path(QModelIndex current) const {
return current.isValid() ? path(current.parent())+QString("/%1,%2").arg(current.row()).arg(current.column()) : "";
}
bool handleError(QIdentifierLiteral e, QString context = QString()) const { return errorHandler ? errorHandler(e, QString("at %1 %2").arg(path(parent)).arg(context)) : false; }
QBuffer io;
TItemReader r;
......
......@@ -101,13 +101,13 @@ public:
Q_ENABLE_MOVE_DEFAULT(QSettingsReader)
QSettingsReader(QSettings* s) : settings(s) { Q_ASSERT(s); levels.push(Level(qBindExpectedItem)); }
QAsciiData currentPath() {
QByteArray path;
Q_FOREACH(Level l, levels) {
if (l.key.isNull()) { path.append('/').append( l.key.utf8() ); }
else { path.append('/').append(QByteArray::number(l.idx )); }
QString currentPath() const {
QString path;
for (auto&& l : levels) {
if (l.key.isNull()) { path.append('/').append(l.key.latin1()); }
else { path.append('/').append(QString::number(l.idx)); }
}
return QAsciiData(path);
return path;
}
// Shortcuts
......@@ -143,7 +143,7 @@ protected:
bool tryAny() { return true; }
bool isValid() const noexcept { return settings; }
bool handleError(QIdentifierLiteral error, QString context = QString()) { return errorHandler ? errorHandler(error, QString(currentPath().latin1()).append(context)) : false; }
bool handleError(QIdentifierLiteral e, QString context = QString()) const { return errorHandler ? errorHandler(e, QString("at %1 %2").arg(currentPath(), context)) : false; }
private:
template<typename T>
bool set(T& t, QIdentifierLiteral error) { QVariant v = settings->value(key()); if (v.convert(qMetaTypeId<T>())) { t = v.value<T>(); return true; } handleError(error); return false; }
......
#include "QValue.h"
QIdentifierLiteral qBindUnexpectedValue ("UnexpectedValue" );
QIdentifierLiteral qBindUnexpectedEnd ("qBindUnexpectedEnd" );
QIdentifierLiteral qBindUnexpectedEnd ("UnexpectedEnd" );
QIdentifierLiteral qBindBlocked ("Blocked" );
QIdentifierLiteral qBindExpectedItem ("ExpectedItem" );
QIdentifierLiteral qBindExpectedNull ("ExpectedNull" );
......
......@@ -54,6 +54,7 @@
extern QIdentifierLiteral qBindUnexpectedValue; //!< Values that cannot be bound (invalid or not supported by the bound data type or our data model)
extern QIdentifierLiteral qBindUnexpectedEnd;
extern QIdentifierLiteral qBindBlocked;
extern QIdentifierLiteral qBindExpectedItem;
extern QIdentifierLiteral qBindExpectedNull;
......@@ -227,8 +228,12 @@ struct QAbstractValue {
//! \warning meta() is ignored by default and subject to various interpretation, so users should define or adopt existing meta data standards like XSD for sake of interoperability
virtual void _meta(QIdentifierLiteral&, QAsciiData&) {}
virtual QValueErrorHandler setErrorHandler(QValueErrorHandler newHandler = nullptr) { auto previousHandler = errorHandler; errorHandler = newHandler; return previousHandler; }
virtual bool handleError(QIdentifierLiteral e, QString context = QString()) = 0;
virtual QValueErrorHandler setErrorHandler(QValueErrorHandler newHandler = nullptr) {
auto previousHandler = errorHandler;
errorHandler = newHandler;
return previousHandler;
}
virtual bool handleError(QIdentifierLiteral e, QString context = QString()) const { return errorHandler ? errorHandler(e, context) : false; }
protected:
QValueErrorHandler errorHandler = nullptr;
};
......@@ -281,7 +286,7 @@ public:
QValueMode mode() const noexcept { return impl ? impl->mode() : QValueMode::Invalid; }
explicit operator bool() const noexcept { return isValid(); }
bool isValid() const noexcept { return impl && impl->isValid(); } //!< Drives QTransmogrifier<T>::bind() traversal
QCur* operator ->() noexcept { return this; }
QCur* operator->() noexcept { return this; }
QVal<QCur> value() noexcept ;
operator QValueEnd() {
bool isValid = impl && impl->isValid();
......@@ -290,15 +295,15 @@ public:
}
QValueErrorHandler setErrorHandler(QValueErrorHandler newHandler = nullptr) { return Q_LIKELY(impl) ? impl->setErrorHandler(newHandler) : nullptr; }
bool handleError(QIdentifierLiteral name, QString context = QString()) { return Q_LIKELY(impl) ? impl->handleError(name, context) : false; }
bool handleError(const char* asciiName, QString context = QString()) { return handleError(QIdentifierLiteral(asciiName), context); }
bool handleError(QIdentifierLiteral name, QString context = QString()) const { return Q_LIKELY(impl) ? impl->handleError(name, context) : false; }
bool handleError(const char* asciiName, QString context = QString()) const { return handleError(QIdentifierLiteral(asciiName), context); }
protected:
template<class T_> friend class QVal; // enables calling methods below
template<class T_> friend class QSeq;
template<class T_> friend class QRec;
void _meta (QIdentifierLiteral& n,
QAsciiData& m) { if (Q_LIKELY(impl) ) impl->_meta (n,m); } //!< idempotent (can be called by more than one code)
void _meta (QIdentifierLiteral& n,
QAsciiData& m) { if (Q_LIKELY(impl) ) impl->_meta (n,m); } //!< idempotent (can be called by more than one code)
bool trySequence( quint32* s=nullptr) { return Q_LIKELY(impl) && impl->trySequence(s); }
bool tryRecord ( quint32* s=nullptr) { return Q_LIKELY(impl) && impl->tryRecord (s); }
bool tryNull ( ) { return Q_LIKELY(impl) && impl->tryNull ( ); }
......@@ -318,6 +323,11 @@ private:
bool tryItem( ) { return Q_LIKELY(impl) && impl->tryItem( ); }
bool tryOut ( ) { return Q_LIKELY(impl) && impl->tryOut ( ); }
private:
template<class T> friend class QVal;
template<class T> friend class QSeq;
template<class T> friend class QRec;
void invalidate() noexcept { impl = nullptr; }
QCur _unsafeCopy() noexcept { return impl ? QCur(impl) : QCur(); }
QAbstractValue* impl = nullptr;
......@@ -356,11 +366,19 @@ template<class T_> class QVal
Q_DISABLE_COPY(QVal)
public:
Q_ENABLE_MOVE_DEFAULT(QVal)
explicit QVal(T_&& out) noexcept { std::swap(outer, out); }
~QVal() {
if (isValid()) {
if (outer->handleError(qBindUnexpectedEnd) && !outer->tryAny()) {
Q_ASSERT_X(outer->handleError(qBindBlocked), Q_FUNC_INFO, "Unhandled blocked bind");
outer.invalidate();
}
}
}
explicit QVal(T_&& out) noexcept { outer = std::move(out); }
explicit operator bool() const noexcept { return isValid(); }
bool isValid() const noexcept { return outer.operator bool(); } //!< Drives QTransmogrifier<T>::bind() traversal
QCur* operator->() noexcept { return outer.operator ->(); }
bool isValid() const noexcept { return outer.isValid(); } //!< Drives QTransmogrifier<T>::bind() traversal
QCur* operator->() noexcept { return outer.operator->(); }
QVal<T_> meta (QIdentifierLiteral& n, QAsciiData& m) { outer->_meta(n,m); return std::move(*this); }
......@@ -406,10 +424,17 @@ template<class T_> class QSeq
Q_DISABLE_COPY(QSeq)
public:
Q_ENABLE_MOVE_DEFAULT(QSeq)
~QSeq() {if (operator QValueEnd().isValid()) { Q_UNUSED(outer->handleError(qBindUnexpectedEnd)); } }
~QSeq() {
if (isValid()) {
if (outer->handleError(qBindUnexpectedEnd) && !outer->tryOut()) {
Q_ASSERT_X(outer->handleError(qBindBlocked), Q_FUNC_INFO, "Unhandled blocked bind");
outer.invalidate();
}
}
}
explicit operator bool() const noexcept { return isValid(); }
bool isValid() const noexcept { return outer.operator bool(); } //!< Drives QTransmogrifier<T>::bind() traversal
bool isValid() const noexcept { return outer.isValid(); } //!< Drives QTransmogrifier<T>::bind() traversal
QCur* operator->() noexcept { return outer.operator ->(); }
operator QValueEnd() { return out(); /* calls T_::operator QValueEnd() if T_ != QValueEnd */ }
......@@ -428,7 +453,7 @@ public:
/**/ QSeq<T_> bind ( QLatin1String l) { return item().bind (l) ; }
/**/ QSeq<T_> bind ( QStringView u) { return item().bind (u) ; }
template<typename T> QSeq<T_> bind ( T&& t) { return item().bind(std::forward<T>(t)); }
template<typename T> QSeq<T_> bind ( T&& t) { return item().bind( std::forward<T>( t)); }
template<typename T> QRec<T_> bind (T& t, T&& defaultT) { return item().bind(t,std::forward<T>(defaultT)); }
// Custom bind support
......@@ -440,8 +465,11 @@ public:
// Shortcut
template<typename T> QSeq<T_> operator<<(T&& t) { return item().bind(std::forward<T>(t)); } // stream compatible
private:
template<class T_> friend class QVal;
explicit QSeq(T_&& out) noexcept { std::swap(outer, out); }
template<class T> friend class QRec;
void invalidate() noexcept { outer.invalidate(); }
template<class T> friend class QVal;
explicit QSeq(T_&& out) noexcept { outer = std::move(out); }
template<typename T, typename TEnabledIf> friend struct QTransmogrifier;
QValue unsafeItem() noexcept { return outer->tryItem() ? QValue(outer->_unsafeCopy()) : QValue(); }
......@@ -455,10 +483,17 @@ template<class T_> class QRec
Q_DISABLE_COPY(QRec)
public:
Q_ENABLE_MOVE_DEFAULT(QRec)
~QRec() { if (operator QValueEnd().isValid()) { Q_UNUSED(outer->handleError(qBindUnexpectedEnd)); } }
~QRec() {
if (isValid()) {
if (outer->handleError(qBindUnexpectedEnd) && !outer->tryOut()) {
Q_ASSERT_X(outer->handleError(qBindBlocked), Q_FUNC_INFO, "Unhandled blocked bind");
outer.invalidate();
}
}
}
explicit operator bool() const noexcept { return isValid(); }
bool isValid() const noexcept { return outer.operator bool(); } //!< Drives QTransmogrifier<T>::bind() traversal
bool isValid() const noexcept { return outer.isValid(); } //!< Drives QTransmogrifier<T>::bind() traversal
QCur* operator->() noexcept { return outer.operator ->(); }
operator QValueEnd() { return out(); /* calls T_::operator QValueEnd() if T_ != QValueEnd */ }
......@@ -476,15 +511,26 @@ public:
/**/ QRec<T_> bind (const char* n, QLatin1String l) { auto i = item(n); if (i) { return i.bind (l) ; } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return QRec<T_> (); }
/**/ QRec<T_> bind (const char* n, QStringView u) { auto i = item(n); if (i) { return i.bind (u) ; } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return QRec<T_> (); }
template<typename T> QRec<T_> bind (const char* n, T&& t) { auto i = item(n); if (i) { return i.bind( std::forward<T>( t)); } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return QRec<T_>(); }
template<typename T> QRec<T_> bind (const char* n, T&& t) {
auto i = item(n);
if (i) {
auto r = i.bind( std::forward<T>( t));
return r;
}
Q_UNUSED(outer->handleError(qBindIgnoredItem, n));
return QRec<T_>();
}
template<typename T> QRec<T_> bind (const char* n, T& t, T&& defaultT) { auto i = item(n); if (i) { return i.bind(t, std::forward<T>(defaultT)); } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return QRec<T_>(); }
// Custom bind support
/**/ QRec<T_> with (const char* n, QValLambda customBind) { auto i = item(n); if (i) { return i.with( customBind); } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return QRec<T_>(); }
template<typename T> QRec<T_> with (const char* n, T& t, QValFunction<T> customBind) { auto i = item(n); if (i) { return i.bind(t, customBind); } Q_UNUSED(outer->handleError(qBindIgnoredItem, n)); return QRec<T_>(); }
private:
template<class T_> friend class QVal;
explicit QRec(T_&& out) noexcept { std::swap(outer, out); }
template<class T> friend class QSeq;
void invalidate() noexcept { outer.invalidate(); }
template<class T> friend class QVal;
explicit QRec(T_&& out) noexcept { outer = std::move(out); }
template<typename T, typename TEnabledIf> friend struct QTransmogrifier;
QValue unsafeItem(QIdentifier& n) noexcept { return outer->tryItem(n) ? QValue(outer._unsafeCopy()) : QValue(); }
......@@ -672,8 +718,6 @@ struct QAbstractValueWriter : public QAbstractValue
virtual bool tryBind(const QVariant& r) { QVariant copy(r); return tryBind(std::move(copy)); }
virtual bool tryAny() { return tryNull(); }
virtual bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, context) : true; }
};
//! Base QAbstractValue implementations with QValueMode::Read
......@@ -713,16 +757,16 @@ struct QAbstractValueReader : public QAbstractValue
virtual bool tryBind( QVariant& dst) {
auto suspended = setErrorHandler();
quint32 size=0; QIdentifier key; QVariant item;
if (trySequence(&size)) { QVariantList l; while (tryItem( )) { l.append( tryBind(item) ? item : QVariant()); } dst = l; return tryOut(); }
if (tryRecord (&size)) { QVariantMap l; while (tryItem(key)) { l.insert(key.latin1(), tryBind(item) ? item : QVariant()); } dst = l; return tryOut(); }
bool b; if (tryBind( b)) { dst = QVariant(b); return true; }
quint64 u; if (tryBind( u)) { dst = QVariant(u); return true; }
qint64 l; if (tryBind( l)) { dst = QVariant(l); return true; }
double d; if (tryBind( d)) { dst = QVariant(d); return true; }
QByteArray ba; if (tryBind(ba)) { toVariant(dst,ba); return true; }
QUtf8Data u8; if (tryBind(u8)) { dst = QVariant::fromValue(u8); return true; }
QString t; if (tryBind( t)) { dst = QVariant(t); return true; }
/**/ if (tryNull( )) { dst = QVariant::fromValue(nullptr); return true; }
if (trySequence(&size)) { QVariantList l; while (tryItem( )) { l.append( tryBind(item) ? item : QVariant()); } dst = l; setErrorHandler(suspended); return tryOut(); }
if (tryRecord (&size)) { QVariantMap l; while (tryItem(key)) { l.insert(key.latin1(), tryBind(item) ? item : QVariant()); } dst = l; setErrorHandler(suspended); return tryOut(); }
bool b; if (tryBind( b)) { dst = QVariant(b); setErrorHandler(suspended); return true; }
quint64 u; if (tryBind( u)) { dst = QVariant(u); setErrorHandler(suspended); return true; }
qint64 l; if (tryBind( l)) { dst = QVariant(l); setErrorHandler(suspended); return true; }
double d; if (tryBind( d)) { dst = QVariant(d); setErrorHandler(suspended); return true; }
QByteArray ba; if (tryBind(ba)) { toVariant(dst,ba); setErrorHandler(suspended); return true; }
QUtf8Data u8; if (tryBind(u8)) { dst = QVariant::fromValue(u8); setErrorHandler(suspended); return true; }
QString t; if (tryBind( t)) { dst = QVariant(t); setErrorHandler(suspended); return true; }
/**/ if (tryNull( )) { dst = QVariant::fromValue(nullptr); setErrorHandler(suspended); return true; }
setErrorHandler(suspended);
if (tryAny()) { dst = QVariant(); return true; }
return false;
......@@ -764,6 +808,7 @@ struct QAbstractValueReader : public QAbstractValue
auto suspended = setErrorHandler();
if ( (trySequence() && tryOut())
||(tryRecord () && tryOut())) {
setErrorHandler(suspended);
return true;
}
bool b; // usually easier to check than types below
......@@ -777,8 +822,6 @@ struct QAbstractValueReader : public QAbstractValue
setErrorHandler(suspended);
return bound || handleError(qBindUnexpectedValue);
}
virtual bool handleError(QIdentifierLiteral e, QString context = QString()) { return errorHandler ? errorHandler(e, context) : false; }
protected:
bool toByteArray(QByteArray& b, QUtf8Data s) {
const QByteArray& bytes = s.utf8();
......@@ -1178,6 +1221,7 @@ struct QTransmogrifier<QValue> {
srcVal.bind(std::move(dstVal));
}
src->setErrorHandler(suspended);
/**/ dstSeq.out();
return srcSeq.out();
}
......@@ -1191,6 +1235,7 @@ struct QTransmogrifier<QValue> {
srcVal.bind(std::move(dstVal));
}
src->setErrorHandler(suspended);
/**/ dstRec.out();
return srcRec.out();
}
......
......@@ -123,7 +123,7 @@ public:
QVariantVisitor(const QVariant* v) : value(v) { Q_ASSERT(v); }
void reset(const QVariant* v) { value=v; Q_ASSERT(v); levels.resize(0); }
QAsciiData currentPath() {
QAsciiData currentPath() const {
QByteArray path;
Q_FOREACH(Level l, levels) {
if (l.key.isNull()) { path.append('{').append( l.key.utf8() ); }
......@@ -178,7 +178,7 @@ protected:
bool tryAny () { return true; }
bool isValid() const noexcept { return value; }
bool handleError(QIdentifierLiteral error, QString context = QString()) { return errorHandler ? errorHandler(error, QString(currentPath().latin1()).append(context)) : false; }
bool handleError(QIdentifierLiteral e, QString context = QString()) const { return errorHandler ? errorHandler(e, QString(currentPath().latin1()).append(context)) : false; }
private:
const QVariant* current(unsigned outer=0) const { return unsigned(levels.size())-outer <= 0 ? value : &(levels[unsigned(levels.size())-outer-1].item); }
......
......@@ -86,10 +86,10 @@ protected:
bool tryBind ( double&& n) { static QString s; s.setNum( n ,'g',std::numeric_limits<double>::max_digits10); writeText("decimal" , s); return true; } // with specific precision
bool tryBind ( QByteArray&& s) { QString h; h.reserve(s.size()*2+2+1); h.append("0x").append(s.toHex().toUpper()) ; writeText("hexBinary", h); return true; }
bool isValid() const noexcept { return !io->hasError(); }
bool isValid() const noexcept { return !io->hasError() || handleError(qBindUnexpectedEnd, io->device()->errorString()); }
bool tryOut ( ) { io->writeEndElement(); return true; }
bool tryItem( ) { return true; }
bool tryOut () { io->writeEndElement(); return true; }
bool tryItem() { return true; }
bool tryItem(QIdentifier& n) { name=n; return true; }
void _meta(QIdentifierLiteral &n, QAsciiData &m);
private:
......@@ -101,7 +101,17 @@ private:
QAsciiData& operator[](QIdentifierLiteral n) { return find(n)->second; }
};
void writeText(const char* def, QString text) { io->writeStartElement(tag(def).latin1()); att(); io->writeCharacters(text); io->writeEndElement(); }
void writeText(const char* def, const QString& text) {
io->writeStartElement(tag(def).latin1());
att();
QString valid;
valid.reserve(text.size());
for (QChar c : text) {
valid.append(c<'\x20' && c!='\x9' && c!='\xA' && c!='\xD' ? '?' : c);
}
io->writeCharacters(valid);
io->writeEndElement();
}
QIdentifier tag(const char* def) { if (name.isNull()) { return QIdentifier(def); } auto n=name; name=QIdentifier(); return n; }
void att() { for (auto&& a : atts) { if (!a.second.isNull()) io->writeAttribute(a.first.latin1(), a.second.latin1()); } }
......
......@@ -401,7 +401,13 @@ int main(int argc, char *argv[])
qRegisterMetaTypeStreamOperators<QUtf8Data>();
GROUP("builtin>")//========================================================
QStringList errors;
auto handler = [&errors](QIdentifierLiteral error, QString context) {
errors.append(context.append(error.latin1()));
return true;