From 5d54f9ea2bcf97be6990cae0245b046ffe3bcb0f Mon Sep 17 00:00:00 2001 From: Alexei Rousskikh Date: Mon, 27 Feb 2012 17:02:10 -0500 Subject: implemented array in 'items' attribute; 'additionalItems' attribute Change-Id: Id6e040041253907f1e19f4bfa6c6b4d75dd4d3a4 Reviewed-by: Andrew Christian --- src/qtjsonschema/checkpoints_p.h | 103 ++++++++++++++++++++++++++++--- src/qtjsonschema/schemaobject_p.h | 8 ++- tests/auto/jsonschema/tst_jsonschema.cpp | 42 +++++++++++-- 3 files changed, 137 insertions(+), 16 deletions(-) diff --git a/src/qtjsonschema/checkpoints_p.h b/src/qtjsonschema/checkpoints_p.h index 045cf0e..e30b009 100644 --- a/src/qtjsonschema/checkpoints_p.h +++ b/src/qtjsonschema/checkpoints_p.h @@ -343,9 +343,9 @@ public: } } - if (Check::m_data->m_additionalPropertySchema && 0 == checks.count() && !m_checks.keys().contains(key)) { + if (Check::m_data->m_additionalSchema && 0 == checks.count() && !m_checks.keys().contains(key)) { // do an extra property check if a property does not exist in the schema - if (!Check::m_data->m_additionalPropertySchema->check(property, Check::m_schema->m_callbacks)) { + if (!Check::m_data->m_additionalSchema->check(property, Check::m_schema->m_callbacks)) { return false; } } @@ -427,7 +427,7 @@ public: Object obj = _value.toObject(&ok); if (ok) { // create extra check for additional properties - create new schema - Check::m_data->m_additionalPropertySchema = QSharedPointer< Schema >(new Schema(obj, schema->m_callbacks)); + Check::m_data->m_additionalSchema = QSharedPointer< Schema >(new Schema(obj, schema->m_callbacks)); } } Q_ASSERT(ok); @@ -445,20 +445,30 @@ template class SchemaPrivate::CheckItems : public Check { public: CheckItems(SchemaPrivate *schemap, QSharedPointer &data, const Value &schema) - : Check(schemap, data, "Items check failed for %1") + : Check(schemap, data, "Items check failed for %1"), m_bList(false) { // qDebug() << Q_FUNC_INFO << this; bool ok; Object obj = schema.toObject(&ok); if (ok) { - m_schema = Schema(obj, schemap->m_callbacks); + m_schema.append(Schema(obj, schemap->m_callbacks)); } else { ValueList list = schema.toList(&ok); if (ok) { - Q_ASSERT(false); // TODO + m_bList = true; + if (list.count() > 1) + m_schema.reserve(list.count()); + typename ValueList::const_iterator it; + for (it = list.constBegin(); it != list.constEnd(); ++it) { + Object obj = (*it).toObject(&ok); + if (ok) { + m_schema.append(Schema(obj, schemap->m_callbacks)); + } + } } } + Check::m_data->m_flags |= CheckSharedData::HasItems; Q_ASSERT(ok); } @@ -470,16 +480,87 @@ public: if (!ok) return false; + bool bNoAdditional; + if ((bNoAdditional = Check::m_data->m_flags.testFlag(CheckSharedData::NoAdditionalItems))) { + if (m_bList && array.count() > m_schema.size()) + return false; // AdditionalItems is set to false + } + + int nCnt(0), nIndex(0); + Schema & schema = m_schema[0]; typename ValueList::const_iterator i; - for (i = array.constBegin(); i != array.constEnd(); ++i) { - if (!m_schema.check(*i, Check::m_schema->m_callbacks)) { + for (i = array.constBegin(); i != array.constEnd(); ++i, nCnt++) { + if (m_bList) { + // for a list each item should have a matching schema + if (m_schema.size() > nCnt) { + nIndex = nCnt; + schema = m_schema[nIndex]; + } + else if (Check::m_data->m_additionalSchema) { + schema = *Check::m_data->m_additionalSchema; + } + else { + return true; // nothing to validate with + } + } + + if (!schema.check(*i, Check::m_schema->m_callbacks)) { return false; } } return true; } private: - Schema m_schema; + QVarLengthArray, 1> m_schema; + bool m_bList; +}; + +// 5.6 +template +class SchemaPrivate::CheckAdditionalItems : public Check { +public: + CheckAdditionalItems(SchemaPrivate *schema, QSharedPointer &data, const Value &_value) + : Check(schema, data, "Additional items check failed for %1") + { + bool ok, bAdditional = _value.toBoolean(&ok); + if (ok && !bAdditional) { + Check::m_data->m_flags |= CheckSharedData::NoAdditionalItems; + } + else if (!ok) { + // object if not bool + Object obj = _value.toObject(&ok); + if (ok) { + // create extra check for additional properties - create new schema + Check::m_data->m_additionalSchema = QSharedPointer< Schema >(new Schema(obj, schema->m_callbacks)); + } + } + Q_ASSERT(ok); + } + + virtual bool doCheck(const Value &value) + { + // most of the time a check is done inside CheckItems::doCheck + if (!Check::m_data->m_flags.testFlag(CheckSharedData::HasItems)) { // items attribute is absent so do a check here + if (Check::m_data->m_flags.testFlag(CheckSharedData::NoAdditionalItems)) { + // items attribute is absent, but additionalItems is set to false + return false; + } + else if (Check::m_data->m_additionalSchema) { + 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 (!Check::m_data->m_additionalSchema->check(*i, Check::m_schema->m_callbacks)) { + return false; + } + } + } + } + return true; + } }; // 5.7 @@ -1137,6 +1218,10 @@ typename SchemaPrivate::Check *SchemaPrivate::createCheckPoint(const Key & if (QString::fromLatin1("items") == keyName) return new CheckItems(this, data, value); break; + case QStaticStringHash<'a','d','d','i','t','i','o','n','a','l','i','t','e','m','s'>::Hash: + if (QString::fromLatin1("additionalitems") == keyName) + return new CheckAdditionalItems(this, data, value); + break; case QStaticStringHash<'e','x','t','e','n','d','s'>::Hash: if (QString::fromLatin1("extends") == keyName) return new CheckExtends(this, data, value); diff --git a/src/qtjsonschema/schemaobject_p.h b/src/qtjsonschema/schemaobject_p.h index e8c3516..b230e5a 100644 --- a/src/qtjsonschema/schemaobject_p.h +++ b/src/qtjsonschema/schemaobject_p.h @@ -160,12 +160,14 @@ class SchemaPrivate : public QSharedData NoFlags = 0x0, ExclusiveMinimum = 0x1, ExclusiveMaximum = 0x2, - NoAdditionalProperties = 0x4 + NoAdditionalProperties = 0x4, + NoAdditionalItems = 0x8, + HasItems = 0x10 }; Q_DECLARE_FLAGS(Flags, Flag) Flags m_flags; QSharedPointer m_default; // keeps a default value - QSharedPointer< Schema > m_additionalPropertySchema; + QSharedPointer< Schema > m_additionalSchema; }; class Check { @@ -216,6 +218,8 @@ class SchemaPrivate : public QSharedData class CheckAdditionalProperties; // 5.5 class CheckItems; + // 5.6 + class CheckAdditionalItems; // 5.7 class CheckRequired; // 5.9 diff --git a/tests/auto/jsonschema/tst_jsonschema.cpp b/tests/auto/jsonschema/tst_jsonschema.cpp index c11e001..522fb00 100644 --- a/tests/auto/jsonschema/tst_jsonschema.cpp +++ b/tests/auto/jsonschema/tst_jsonschema.cpp @@ -60,7 +60,8 @@ private slots: void testAdditionalPropertiesValidation(); // 5.5 items void testItemsValidation(); - // TODO: 5.6 additionalItems + // 5.6 additionalItems + void testAdditionalItems(); // 5.7 void testRequiredValidation(); // TODO: 5.8 dependencies @@ -228,17 +229,17 @@ void tst_JsonSchema::testAdditionalPropertiesValidation() array.append(QLatin1String("foo")); array.append(QLatin1String("two")); //["foo", "two"] QVERIFY(validate(array, "{ \"items\" : { \"type\" : \"string\" }, \"additionalProperties\" : false }")); -#ifdef FIX QVERIFY(validate(array, "{ \"items\" : [ { \"type\" : \"string\" }, { \"type\" : \"string\" } ], \"additionalProperties\" : false }")); array.append(3); // ["foo", "two", 3] QVERIFY(validate(array, "{ \"items\" : [ { \"type\" : \"string\" }, { \"type\" : \"string\" } ], \"additionalProperties\" : { \"type\" : \"number\" } }")); + QVERIFY(!validate(array, + "{ \"items\" : [ { \"type\" : \"string\" }, { \"type\" : \"string\" } ], \"additionalProperties\" : { \"type\" : \"string\" } }")); array[2] = QLatin1String("three"); // ["foo", "two", "three"] QVERIFY(validate(array, "{ \"items\" : [ { \"type\" : \"string\" }, { \"type\" : \"string\" }, { \"type\" : \"string\" } ], \"additionalProperties\" : { \"type\" : \"number\" } }")); -#endif } // 5.5 items @@ -254,7 +255,7 @@ void tst_JsonSchema::testItemsValidation() array.append(2); // ["foo", 2] QVERIFY(!validate(array, "{ \"type\" : \"array\", \"items\" : { \"type\" : \"string\" } }")); // INVALID QVERIFY(!validate(array, "{ \"type\" : \"array\", \"items\" : { \"type\" : \"number\" } }")); // INVALID -//fix QVERIFY(validate(array, "{ \"type\" : \"array\", \"items\" : [{ \"type\" : \"string\" }, { \"type\" : \"number\" }] }")); + QVERIFY(validate(array, "{ \"type\" : \"array\", \"items\" : [{ \"type\" : \"string\" }, { \"type\" : \"number\" }] }")); array.removeAt(0); // [2] QVERIFY(!validate(array, "{ \"type\" : \"array\", \"items\" : { \"type\" : \"string\" } }")); // INVALID @@ -263,7 +264,38 @@ void tst_JsonSchema::testItemsValidation() array.append(QLatin1String("foo")); array.append(QLatin1String("two")); //["foo", "two"] // should fail!!!!! -//fix QVERIFY(!validate(array, "{ \"type\" : \"array\", \"items\" : [{ \"type\" : \"string\" }, { \"type\" : \"number\" }] }")); + QVERIFY(!validate(array, "{ \"type\" : \"array\", \"items\" : [{ \"type\" : \"string\" }, { \"type\" : \"number\" }] }")); +} + +// 5.6 additionalItems +void tst_JsonSchema::testAdditionalItems() +{ + //array tests + QJsonArray array; + array.append(1); // [1] + array.append(2); // [1, 2] + array.append(3); // [1, 2, 3] + + QVERIFY(validate(array, "{ \"additionalItems\" : true }")); + QVERIFY(validate(array, "{ \"additionalItems\" : { \"type\" : \"number\" } }")); + QVERIFY(!validate(array, "{ \"additionalItems\" : false }")); + QVERIFY(!validate(array, "{ \"additionalItems\" : { \"type\" : \"string\" } }")); + + array = QJsonArray(); + array.append(QLatin1String("1")); // ['1'] + array.append(QLatin1String("2")); // ['1','2'] + + QVERIFY(validate(array, "{ \"items\" : { \"type\" : \"string\" }, \"additionalItems\" : false }")); + QVERIFY(validate(array, "{ \"items\" : [ { \"type\" : \"string\" }, { \"type\" : \"string\" } ], \"additionalItems\" : false }")); + + array.append(3); // ['1', '2', 3] + QVERIFY(validate(array, "{ \"items\" : [ { \"type\" : \"string\" }, { \"type\" : \"string\" } ], \"additionalItems\" : { \"type\" : \"number\" } }")); + + array[2] = QLatin1String("3"); // ['1', '2', '3'] + QVERIFY(validate(array, "{ \"items\" : [ { \"type\" : \"string\" }, { \"type\" : \"string\" }, { \"type\" : \"string\" } ], \"additionalItems\": { \"type\" : \"number\" } }")); + + QVERIFY(!validate(array, "{ \"items\" : [ { \"type\" : \"string\" }, { \"type\" : \"string\" } ],\"additionalItems\": false }")); + QVERIFY(!validate(array, "{ \"items\" : [ { \"type\" : \"string\" }, { \"type\" : \"string\" } ],\"additionalItems\": { \"type\" : \"number\" } }")); } // 5.7 -- cgit v1.2.3