summaryrefslogtreecommitdiffstats
path: root/src/partition/jsondbcheckpoints_p.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/partition/jsondbcheckpoints_p.h')
-rw-r--r--src/partition/jsondbcheckpoints_p.h930
1 files changed, 930 insertions, 0 deletions
diff --git a/src/partition/jsondbcheckpoints_p.h b/src/partition/jsondbcheckpoints_p.h
new file mode 100644
index 00000000..f1d30f15
--- /dev/null
+++ b/src/partition/jsondbcheckpoints_p.h
@@ -0,0 +1,930 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtAddOn.JsonDb module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef JSONDB_CHECKPOINTS_P_H
+#define JSONDB_CHECKPOINTS_P_H
+
+#include <QtCore/qhash.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qregexp.h>
+#include <QtCore/qregularexpression.h>
+#include <QtCore/qurl.h>
+#include <QtCore/quuid.h>
+
+QT_BEGIN_HEADER
+
+namespace SchemaValidation {
+
+/**
+ \internal
+ This template is used for hash computation for static latin1 strings.
+ */
+template<ushort C1 = 0, ushort C2 = 0, ushort C3 = 0, ushort C4 = 0, ushort C5 = 0,
+ ushort C6 = 0, ushort C7 = 0, ushort C8 = 0, ushort C9 = 0, ushort C10 = 0,
+ ushort C11 = 0, ushort C12 = 0, ushort C13 = 0, ushort C14 = 0, ushort C15 = 0,
+ ushort C16 = 0, ushort C17 = 0, ushort C18 = 0>
+struct QStaticStringHash
+{
+ typedef QStaticStringHash<C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15, C16, C17, C18> Suffix;
+
+ const static int Hash = (C1 ^ Suffix::Hash) + 5;
+ //(C1 ^ ( (C2 ^ (...)) +5 )) +5
+};
+
+template<>
+struct QStaticStringHash<>
+{
+ typedef QStaticStringHash<> Suffix;
+ const static int Hash = 0;
+
+ /**
+ \internal
+ This function has to be align with qStringHash::Hash value
+ */
+ inline static int hash(const QString &string)
+ {
+ const ushort *str = reinterpret_cast<const ushort*>(string.constData());
+ return hash(str, 0, string.length());
+ }
+private:
+ inline static int hash(const ushort *str, const int index, const int length)
+ {
+ return index != length ? (str[index] ^ hash(str, index + 1, length)) + 5
+ : 0;
+ }
+};
+
+template<class T>
+class SchemaPrivate<T>::NullCheck : public Check {
+public:
+ NullCheck(SchemaPrivate *schema)
+ : Check(schema, "") // TODO
+ {}
+protected:
+ virtual bool doCheck(const Value&) { return true; }
+};
+
+// 5.1
+template<class T>
+class SchemaPrivate<T>::CheckType : public Check {
+ enum Type {StringType = 0x0001,
+ NumberType = 0x0002,
+ IntegerType = 0x0004,
+ BooleanType = 0x0008,
+ ObjectType = 0x0010,
+ ArrayType = 0x0020,
+ NullType = 0x0040,
+ AnyType = 0x0080,
+ UnknownType = 0};
+public:
+ CheckType(SchemaPrivate *schema, const Value& type)
+ : Check(schema, "Type check failed for %1")
+ , m_type(UnknownType)
+ {
+ bool ok;
+ QStringList typesName;
+
+ QString typeName = type.toString(&ok);
+ if (!ok) {
+ ValueList typesList = type.toList(&ok);
+ Q_ASSERT_X(ok, Q_FUNC_INFO, "Type is neither a string nor an array");
+ typename ValueList::const_iterator i;
+ for (i = typesList.constBegin(); i != typesList.constEnd(); ++i) {
+ typeName = (*i).toString(&ok);
+ if (ok) {
+ typesName << typeName;
+ }
+ }
+ } else {
+ typesName << typeName;
+ }
+ foreach (const QString &name, typesName) {
+ const int hash = QStaticStringHash<>::hash(name.toLower());
+
+ // FIXME there are chances of conflicts, do we care? What is chance for new types in JSON?
+ // FIXME we need to check only 2 chars. That would be faster.
+ // FIXME probably we want to support schemas here too.
+ switch (hash) {
+ case QStaticStringHash<'s','t','r','i','n','g'>::Hash:
+ m_type |= StringType;
+ break;
+ case QStaticStringHash<'n','u','m','b','e','r'>::Hash:
+ m_type |= NumberType;
+ m_type |= IntegerType; // an integer is also a number
+ break;
+ case QStaticStringHash<'i','n','t','e','g','e','r'>::Hash:
+ m_type |= IntegerType;
+ break;
+ case QStaticStringHash<'b','o','o','l','e','a','n'>::Hash:
+ m_type |= BooleanType;
+ break;
+ case QStaticStringHash<'o','b','j','e','c','t'>::Hash:
+ m_type |= ObjectType;
+ break;
+ case QStaticStringHash<'a','r','r','a','y'>::Hash:
+ m_type |= ArrayType;
+ break;
+ case QStaticStringHash<'a','n','y'>::Hash:
+ m_type |= AnyType;
+ break;
+ case QStaticStringHash<'n','u','l','l'>::Hash:
+ m_type |= NullType;
+ break;
+ default:
+ m_type |= UnknownType;
+ }
+ }
+
+// qDebug() << Q_FUNC_INFO << m_type << type.toString(&ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ if (m_type == UnknownType)
+ return true;
+
+ bool result = findType(value) & m_type;
+// bool ok;
+// qDebug() << Q_FUNC_INFO << findType(value) << m_type << value.toString(&ok) << result;
+ return result;
+ }
+
+private:
+ inline Type findType(const Value &value) const
+ {
+ bool ok;
+ // Lets assume that the value is valid.
+ switch (m_type) {
+ case StringType:
+ value.toString(&ok);
+ if (ok)
+ return StringType;
+ break;
+ case NumberType:
+ value.toDouble(&ok);
+ if (ok)
+ return NumberType;
+ break;
+ case IntegerType:
+ value.toInt(&ok);
+ if (ok)
+ return IntegerType;
+ break;
+ case BooleanType:
+ value.toBool(&ok);
+ if (ok)
+ return BooleanType;
+ break;
+ case ObjectType:
+ value.toObject(&ok);
+ if (ok)
+ return ObjectType;
+ break;
+ case NullType:
+ value.toNull(&ok);
+ if (ok)
+ return NullType;
+ break;
+ case AnyType:
+ return AnyType;
+ case UnknownType:
+ break;
+ default:
+ break;
+ };
+
+ //TODO FIXME it can be better
+ value.toInt(&ok);
+ if (ok)
+ return IntegerType;
+ value.toDouble(&ok);
+ if (ok)
+ return NumberType;
+ value.toObject(&ok);
+ if (ok)
+ return ObjectType;
+ value.toString(&ok);
+ if (ok)
+ return StringType;
+ value.toBool(&ok);
+ if (ok)
+ return BooleanType;
+ value.toList(&ok);
+ if (ok)
+ return ArrayType;
+ return AnyType;
+ }
+
+ uint m_type;
+};
+
+// 5.2
+template<class T>
+class SchemaPrivate<T>::CheckProperties : public Check {
+public:
+ CheckProperties(SchemaPrivate *schema, const Value &properties)
+ : Check(schema, "Properties check failed for %1")
+ {
+ bool ok;
+ const Object obj = properties.toObject(&ok);
+ Q_ASSERT(ok);
+
+ QList<Key> propertyNames = obj.propertyNames();
+// qDebug() << " propertyNames: " << propertyNames <<this;
+
+ m_checks.reserve(propertyNames.count());
+ foreach (const Key &propertyName, propertyNames) {
+ QVarLengthArray<Check *, 4> checks;
+ const Object propertyChecks = obj.property(propertyName).toObject(&ok);
+// qDebug() << " propertyChecks:" << propertyChecks;
+
+ Q_ASSERT(ok);
+ foreach (const Key &key, propertyChecks.propertyNames()) {
+// bool ok;
+// qDebug() << " key:" << key << this << propertyChecks.property(key).toString(&ok)<< propertyChecks.property(key).toInt(&ok);
+ checks.append(schema->createCheckPoint(key, propertyChecks.property(key)));
+ }
+ m_checks.insert(propertyName, checks);
+ }
+ }
+
+ ~CheckProperties()
+ {
+ typename QHash<const Key, QVarLengthArray<Check *, 4> >::const_iterator i;
+ for (i = m_checks.constBegin(); i != m_checks.constEnd(); ++i) {
+ typename QVarLengthArray<Check *, 4>::const_iterator j;
+ for (j = i.value().constBegin(); j != i.value().constEnd(); ++j){
+ delete *j;
+ }
+ }
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ Object object = value.toObject(&ok);
+ if (!ok)
+ return false;
+
+ //qDebug() << Q_FUNC_INFO;
+ foreach (const Key &key, object.propertyNames()) {
+ QVarLengthArray<Check *, 4> empty;
+ QVarLengthArray<Check *, 4> checks = m_checks.value(key, empty);
+ Value property = object.property(key);
+ foreach (Check *check, checks) {
+ //qDebug() <<"CHECKING:" << check;
+ if (!check->check(property)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+private:
+ QHash<const Key, QVarLengthArray<Check *, 4> > m_checks;
+};
+
+// 5.5
+template<class T>
+class SchemaPrivate<T>::CheckItems : public Check {
+public:
+ CheckItems(SchemaPrivate *schemap, const Value &schema)
+ : Check(schemap, "Items check failed for %1")
+ {
+ // qDebug() << Q_FUNC_INFO << this;
+ bool ok;
+ Object obj = schema.toObject(&ok);
+ Q_ASSERT(ok);
+ m_schema = Schema<T>(obj, schemap->m_callbacks);
+ }
+
+ virtual bool doCheck(const Value& value)
+ {
+ //qDebug() << Q_FUNC_INFO << this;
+ bool ok;
+ ValueList array = value.toList(&ok);
+ if (!ok)
+ return false;
+
+ typename ValueList::const_iterator i;
+ for (i = array.constBegin(); i != array.constEnd(); ++i) {
+ if (!m_schema.check(*i, Check::m_schema->m_callbacks)) {
+ return false;
+ }
+ }
+ return true;
+ }
+private:
+ Schema<T> m_schema;
+};
+
+// 5.7
+template<class T>
+class SchemaPrivate<T>::CheckRequired : public Check {
+public:
+ CheckRequired(SchemaPrivate *schema, const Value &required)
+ : Check(schema, "Check required field") // TODO what to do about Required ?
+ {
+ bool ok;
+ m_req = required.toBool(&ok);
+ if (!ok) {
+ // maybe someone used string instead of bool
+ QString value = required.toString(&ok).toLower();
+ if (value == QString::fromLatin1("false"))
+ m_req = false;
+ else if (value == QString::fromLatin1("true"))
+ m_req = true;
+ else
+ Q_ASSERT(false);
+
+ qWarning() << QString::fromLatin1("Wrong 'required' syntax found, instead of boolean type a string was used");
+ }
+ Q_ASSERT(ok);
+ if (m_req)
+ Check::m_schema->m_maxRequired++;
+ }
+
+ virtual bool doCheck(const Value&)
+ {
+ //qDebug() << Q_FUNC_INFO << m_schema << this;
+ if (m_req)
+ Check::m_schema->m_requiredCount++;
+ return true;
+ }
+private:
+ bool m_req;
+};
+
+// 5.9
+template<class T>
+class SchemaPrivate<T>::CheckMinimum : public Check {
+public:
+ CheckMinimum(SchemaPrivate *schema, const Value &minimum)
+ : Check(schema, "Minimum check failed for %1")
+ {
+ bool ok;
+ m_min = minimum.toDouble(&ok);
+ Q_ASSERT(ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ return value.toDouble(&ok) >= m_min && ok;
+ }
+private:
+ double m_min;
+};
+
+// 5.10
+template<class T>
+class SchemaPrivate<T>::CheckMaximum : public Check {
+public:
+ CheckMaximum(SchemaPrivate *schema, const Value &maximum)
+ : Check(schema, "Maximum check failed for %1")
+ {
+ // qDebug() << Q_FUNC_INFO << this;
+ bool ok;
+ m_max = maximum.toDouble(&ok);
+ Q_ASSERT(ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ //qDebug() << Q_FUNC_INFO << value << m_max << this;
+ bool ok;
+ return value.toDouble(&ok) <= m_max && ok;
+ }
+private:
+ double m_max;
+};
+
+
+// 5.11
+template<class T>
+class SchemaPrivate<T>::CheckExclusiveMinimum : public Check {
+public:
+ CheckExclusiveMinimum(SchemaPrivate *schema, const Value &minimum)
+ : Check(schema, "Exclusive minimum check failed for %1")
+ {
+ bool ok;
+ m_min = minimum.toDouble(&ok);
+ Q_ASSERT(ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ return value.toDouble(&ok) > m_min && ok;
+ }
+private:
+ double m_min;
+};
+
+// 5.12
+template<class T>
+class SchemaPrivate<T>::CheckExclusiveMaximum : public Check {
+public:
+ CheckExclusiveMaximum(SchemaPrivate *schema, const Value &maximum)
+ : Check(schema, "Exclusive minimum check failed for %1")
+ {
+ bool ok;
+ m_max = maximum.toDouble(&ok);
+ Q_ASSERT(ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ return value.toDouble(&ok) < m_max && ok;
+ }
+private:
+ double m_max;
+};
+
+// 5.13
+template<class T>
+class SchemaPrivate<T>::CheckMinItems : public Check {
+public:
+ CheckMinItems(SchemaPrivate *schema, const Value& minimum)
+ : Check(schema, "Minimum item count check failed for %1")
+ {
+ bool ok;
+ m_min = minimum.toInt(&ok);
+ Q_ASSERT(ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ int count = value.toList(&ok).size();
+ return count >= m_min && ok;
+ }
+private:
+ int m_min;
+};
+
+// 5.14
+template<class T>
+class SchemaPrivate<T>::CheckMaxItems : public Check {
+public:
+ CheckMaxItems(SchemaPrivate *schema, const Value& maximum)
+ : Check(schema, "Maximum item count check failed for %1")
+ {
+ bool ok;
+ m_max = maximum.toInt(&ok);
+ Q_ASSERT(ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ int count = value.toList(&ok).size();
+ return count <= m_max && ok;
+ }
+
+private:
+ int m_max;
+};
+
+// 5.16
+template<class T>
+class SchemaPrivate<T>::CheckPattern : public Check {
+public:
+ CheckPattern(SchemaPrivate *schema, const Value& patternValue)
+ : Check(schema, "Pattern check failed for %1")
+ {
+ bool ok;
+ QString patternString = patternValue.toString(&ok);
+ m_regexp.setPattern(patternString);
+ Q_ASSERT(ok && m_regexp.isValid());
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ QString str = value.toString(&ok);
+ if (!ok) {
+ // According to spec (5.15) we should check the value only when it exist and it is a string.
+ // It is a bit strange, but I think we have to return true here.
+ return true;
+ }
+ return m_regexp.exactMatch(str);
+ }
+private:
+ QRegExp m_regexp;
+};
+
+// 5.17
+template<class T>
+class SchemaPrivate<T>::CheckMinLength : public Check {
+public:
+ CheckMinLength(SchemaPrivate *schema, const Value& min)
+ : Check(schema, "Minimal string length check failed for %1")
+ {
+ bool ok;
+ m_min = min.toInt(&ok);
+ Q_ASSERT(ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ QString str = value.toString(&ok);
+// qDebug() << Q_FUNC_INFO << str << ok;
+ if (!ok) {
+ // According to spec (5.16) we should check the value only when it exist and it is a string.
+ // It is a bit strange, but I think we have to return true here.
+ return true;
+ }
+ return str.length() >= m_min;
+ }
+private:
+ int m_min;
+};
+
+// 5.18
+template<class T>
+class SchemaPrivate<T>::CheckMaxLength : public Check {
+public:
+ CheckMaxLength(SchemaPrivate *schema, const Value& max)
+ : Check(schema, "Maximal string length check failed for %1")
+ {
+ bool ok;
+ m_max = max.toInt(&ok);
+ Q_ASSERT(ok);
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ QString str = value.toString(&ok);
+// qDebug() << Q_FUNC_INFO << str << ok;
+ if (!ok) {
+ // According to spec (5.16) we should check the value only when it exist and it is a string.
+ // It is a bit strange, but I think we have to return true here.
+ return true;
+ }
+ return str.length() <= m_max;
+ }
+private:
+ int m_max;
+};
+
+// 5.23
+template<class T>
+class SchemaPrivate<T>::CheckFormat : public Check {
+public:
+ CheckFormat(SchemaPrivate *schema, const Value &format)
+ : Check(schema, "String format check failed for %1")
+ {
+ bool ok;
+ m_format = format.toString(&ok);
+ Q_ASSERT(ok);
+ //yyyy-MM-ddTHH:mm:ssZ or yyyy-MM-ddTHH:mm:ss.zzzZ in UTC time
+ m_dateTimeExp.setPattern(QString::fromLatin1("^[0-9]{4}-((0[1-9])|(1[0-2]))-(([0][1-9])|([12][0-9])|(3[01]))T(([01][0-9])|(2[0-3])):[0-5][0-9]:[0-5][0-9]([.][0-9]{3})?Z$"));
+ //yyyy-MM-dd
+ m_dateExp.setPattern(QString::fromLatin1("^([0-9]{4}-((0[1-9])|(1[0-2]))-(([0][1-9])|([12][0-9])|(3[01])))$"));
+ //HH:mm:ss or HH:mm:ss.zzz
+ m_timeExp.setPattern(QString::fromLatin1("^((([01][0-9])|(2[0-3])):[0-5][0-9]:[0-5][0-9]([.][0-9]{3})?)$"));
+ //Checking for dot-separated letters is sufficient, at least one dot is required.
+ m_identifierExp.setPattern(QString::fromLatin1("^([a-z]+([.][a-z]+)+)$"));
+ m_identifierExp.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
+ //Checking for dot-separated digits is sufficient, at least one dot is required.
+ m_versionExp.setPattern(QString::fromLatin1("^([0-9]+([.][0-9]+)+)$"));
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ bool ok;
+ QString str = value.toString(&ok);
+ if (!ok)
+ return false;
+
+ const int hash = QStaticStringHash<>::hash(m_format.toLower());
+
+ switch (hash) {
+ case QStaticStringHash<'d','a','t','e','-','t','i','m','e'>::Hash:
+ //Since "time" shares the same hash number with "date-time", check the string
+ if (m_format == QString::fromLatin1("time")) {
+ if (!m_timeExp.match(str).hasMatch())
+ return false;
+ return true;
+ }
+ if (!m_dateTimeExp.match(str).hasMatch())
+ return false;
+ return true;
+ case QStaticStringHash<'d','a','t','e'>::Hash:
+ if (!m_dateExp.match(str).hasMatch())
+ return false;
+ return true;
+ case QStaticStringHash<'u','u','i','d'>::Hash:
+ m_uuid = QUuid(str);
+ if (m_uuid.isNull())
+ return false;
+ return true;
+ case QStaticStringHash<'r','e','g','e','x'>::Hash:
+ m_regExp.setPattern(str);
+ if (!m_regExp.isValid())
+ return false;
+ return true;
+ case QStaticStringHash<'i','d','e','n','t','i','f','i','e','r'>::Hash:
+ if (!m_identifierExp.match(str).hasMatch())
+ return false;
+ return true;
+ case QStaticStringHash<'v','e','r','s','i','o','n'>::Hash:
+ if (!m_versionExp.match(str).hasMatch())
+ return false;
+ return true;
+ case QStaticStringHash<'n','o','n','n','e','g','a','t','i','v','e','i','n','t','e','g','e','r'>::Hash: {
+ int a = str.toInt(&ok);
+ if (!ok || a < 0)
+ return false;
+ return true;
+ }
+ default:
+ return true;
+ }
+ }
+private:
+ QString m_format;
+ QRegularExpression m_dateTimeExp;
+ QRegularExpression m_dateExp;
+ QRegularExpression m_timeExp;
+ QRegularExpression m_identifierExp;
+ QRegularExpression m_versionExp;
+ QRegularExpression m_regExp;
+ QUuid m_uuid;
+ QUrl m_url;
+};
+
+// 5.26
+template<class T>
+class SchemaPrivate<T>::CheckExtends : public Check {
+public:
+ CheckExtends(SchemaPrivate *schema, const Value &value)
+ : Check(schema, "Extends check failed for %1")
+ {
+ // FIXME
+ // Keep in mind that there is a bug in spec. (internet draft 3).
+ // We should search for a schema not for a string here.
+ // Tests are using "string" syntax, so we need to support it for a while
+ bool ok;
+ Object obj = value.toObject(&ok);
+ if (!ok) {
+ QString schemaName = value.toString(&ok);
+ if (!ok) {
+ ValueList array = value.toList(&ok);
+ Q_ASSERT(ok);
+ typename ValueList::const_iterator i;
+ for (i = array.constBegin(); i != array.constEnd(); ++i) {
+ Object obj = (*i).toObject(&ok);
+ Q_ASSERT(ok);
+ m_extendedSchema.append(Schema<T>(obj, schema->m_callbacks));
+ }
+ } else {
+ qWarning() << QString::fromLatin1("Wrong 'extends' syntax found, instead of \"%1\" should be \"%2\"")
+ .arg(schemaName, QString::fromLatin1("{\"$ref\":\"%1\"}").arg(schemaName));
+ m_extendedSchema.append(schema->m_callbacks->loadSchema(schemaName));
+ }
+ } else {
+ m_extendedSchema.append(Schema<T>(obj, schema->m_callbacks));
+ }
+ }
+
+ virtual bool doCheck(const Value &value)
+ {
+ for (int i = 0; i < m_extendedSchema.count(); ++i) {
+ if (!m_extendedSchema[i].check(value, Check::m_schema->m_callbacks))
+ return false;
+ }
+ return true;
+ }
+private:
+ QVarLengthArray<Schema<T>, 4> m_extendedSchema;
+};
+
+// 5.28
+template<class T>
+class SchemaPrivate<T>::CheckRef : public Check {
+public:
+ CheckRef(SchemaPrivate *schema, const Value &value)
+ : Check(schema, "$Ref check failed for %1")
+ {
+ // TODO according to spec we should replace existing check by this one
+ // I'm not sure what does it mean. Should we remove other checks?
+ // What if we have two $ref? Can it happen? For now, lets use magic of
+ // undefined bahaviour (without crashing of course).
+ bool ok;
+ QString schemaName = value.toString(&ok);
+ Q_ASSERT(ok);
+
+ m_newSchema = schema->m_callbacks->loadSchema(schemaName);
+ if (!m_newSchema.isValid()) {
+ // FIXME should we have current schema name?
+ const QString msg = QString::fromLatin1("Schema extends %1 but it is unknown.")
+ .arg(schemaName);
+ qWarning() << msg;
+ schema->m_callbacks->setError(msg);
+ }
+ }
+ virtual bool doCheck(const Value &value)
+ {
+ bool result = m_newSchema.check(value, Check::m_schema->m_callbacks);
+// qDebug() << Q_FUNC_INFO << result;
+ return result;
+ }
+private:
+ Schema<T> m_newSchema;
+};
+
+template<class T>
+class SchemaPrivate<T>::CheckDescription : public NullCheck {
+public:
+ CheckDescription(SchemaPrivate *schema)
+ : NullCheck(schema)
+ {}
+};
+
+template<class T>
+class SchemaPrivate<T>::CheckTitle : public NullCheck {
+public:
+ CheckTitle(SchemaPrivate *schema)
+ : NullCheck(schema)
+ {}
+};
+
+template<class T>
+typename SchemaPrivate<T>::Check *SchemaPrivate<T>::createCheckPoint(const Key &key, const Value &value)
+{
+ QString keyName = key;
+ keyName = keyName.toLower();
+
+ // This is a perfect hash. BUT spec, in future, can be enriched by new values, that we should just ignore.
+ // As we do not know about them we can't be sure that our perfect hash will be still perfect, therefore
+ // we have to do additional string comparison that confirm result hash function result.
+ int hash = QStaticStringHash<>::hash(keyName);
+ switch (hash) {
+ case QStaticStringHash<'r','e','q','u','i','r','e','d'>::Hash:
+ if (QString::fromLatin1("required") == keyName)
+ return new CheckRequired(this, value);
+ break;
+ case QStaticStringHash<'m','a','x','i','m','u','m'>::Hash:
+ if (QString::fromLatin1("maximum") == keyName)
+ return new CheckMaximum(this, value);
+ break;
+ case QStaticStringHash<'e','x','c','l','u','s','i','v','e','m','a','x','i','m','u','m'>::Hash:
+ if (QString::fromLatin1("exclusivemaximum") == keyName)
+ return new CheckExclusiveMaximum(this, value);
+ break;
+ case QStaticStringHash<'m','i','n','i','m','u','m'>::Hash:
+ if (QString::fromLatin1("minimum") == keyName)
+ return new CheckMinimum(this, value);
+ break;
+ case QStaticStringHash<'e','x','c','l','u','s','i','v','e','m','i','n','i','m','u','m'>::Hash:
+ if (QString::fromLatin1("exclusiveminimum") == keyName)
+ return new CheckExclusiveMinimum(this, value);
+ break;
+ case QStaticStringHash<'p','r','o','p','e','r','t','i','e','s'>::Hash:
+ if (QString::fromLatin1("properties") == keyName)
+ return new CheckProperties(this, value);
+ break;
+ case QStaticStringHash<'d','e','s','c','r','i','p','t','i','o','n'>::Hash:
+ if (QString::fromLatin1("description") == keyName)
+ return new CheckDescription(this);
+ break;
+ case QStaticStringHash<'t','i','t','l','e'>::Hash:
+ if (QString::fromLatin1("title") == keyName)
+ return new CheckTitle(this);
+ break;
+ case QStaticStringHash<'m','a','x','i','t','e','m','s'>::Hash:
+ if (QString::fromLatin1("maxitems") == keyName)
+ return new CheckMaxItems(this,value);
+ break;
+ case QStaticStringHash<'m','i','n','i','t','e','m','s'>::Hash:
+ if (QString::fromLatin1("minitems") == keyName)
+ return new CheckMinItems(this,value);
+ break;
+ case QStaticStringHash<'i','t','e','m','s'>::Hash:
+ if (QString::fromLatin1("items") == keyName)
+ return new CheckItems(this,value);
+ break;
+ case QStaticStringHash<'e','x','t','e','n','d','s'>::Hash:
+ if (QString::fromLatin1("extends") == keyName)
+ return new CheckExtends(this,value);
+ break;
+ case QStaticStringHash<'p','a','t','t','e','r','n'>::Hash:
+ if (QString::fromLatin1("pattern") == keyName)
+ return new CheckPattern(this, value);
+ break;
+ case QStaticStringHash<'m','i','n','l','e','n','g','t','h'>::Hash:
+ if (QString::fromLatin1("minlength") == keyName)
+ return new CheckMinLength(this, value);
+ break;
+ case QStaticStringHash<'m','a','x','l','e','n','g','t','h'>::Hash:
+ if (QString::fromLatin1("maxlength") == keyName)
+ return new CheckMaxLength(this, value);
+ break;
+ case QStaticStringHash<'$','r','e','f'>::Hash:
+ if (QString::fromLatin1("$ref") == keyName)
+ return new CheckRef(this, value);
+ break;
+ case QStaticStringHash<'t','y','p','e'>::Hash:
+ if (QString::fromLatin1("type") == keyName)
+ return new CheckType(this, value);
+ break;
+ case QStaticStringHash<'f','o','r','m','a','t'>::Hash:
+ if (QString::fromLatin1("format") == keyName)
+ return new CheckFormat(this, value);
+ break;
+ default:
+// qDebug() << "NOT FOUND" << keyName;
+ return new NullCheck(this);
+ }
+
+// qDebug() << "FALLBACK" << keyName;
+// bool ok;
+// qCritical() << keyName << value.toString(&ok);
+ return new NullCheck(this);
+}
+
+template<class T>
+bool Schema<T>::check(const Value &value, Service *callbackToUseForCheck) const
+{
+ return d_ptr->check(value, callbackToUseForCheck);
+}
+
+template<class T>
+bool SchemaPrivate<T>::check(const Value &value, Service *callbackToUseForCheck) const
+{
+ //qDebug() << Q_FUNC_INFO << m_checks.count() << this;
+ Q_ASSERT(callbackToUseForCheck);
+ Q_ASSERT(!m_callbacks);
+
+ m_callbacks = callbackToUseForCheck;
+ bool result = check(value);
+ m_callbacks = 0;
+ return result;
+}
+
+template<class T>
+bool SchemaPrivate<T>::check(const Value &value) const
+{
+ Q_ASSERT(m_callbacks);
+
+ m_requiredCount = 0;
+ foreach (Check *check, m_checks) {
+ if (!check->check(value)) {
+ return false;
+ }
+ }
+ if (m_requiredCount != m_maxRequired) {
+ m_callbacks->setError(QString::fromLatin1("Schema validation error: Required field is missing"));
+ return false;
+ }
+ return true;
+}
+
+} // namespace SchemaValidation
+
+QT_END_HEADER
+
+#endif // JSONDB_CHECKPOINTS_P_H