From 5bc9523cbfebf18811849e4ecb84c681f155310b Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Mon, 11 May 2015 13:55:34 +0800 Subject: [PATCH] Add invalid schema/document pointers --- include/rapidjson/schema.h | 80 ++++++++++++++++++++++++++++++------ test/unittest/schematest.cpp | 41 +++++++++++++++--- 2 files changed, 104 insertions(+), 17 deletions(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 0e5868e..614789e 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1035,6 +1035,8 @@ public: typedef internal::Schema SchemaType; typedef GenericPointer PointerType; friend class internal::Schema; + template + friend class GenericSchemaValidator; GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : remoteProvider_(remoteProvider), @@ -1136,6 +1138,13 @@ private: return 0; } + PointerType GetPointer(const SchemaType* schema) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (schema == target->schema) + return target->pointer; + return PointerType(); + } + static const size_t kInitialSchemaMapSize = 64; static const size_t kInitialSchemaRefSize = 64; @@ -1160,19 +1169,21 @@ class GenericSchemaValidator : { public: typedef typename SchemaDocumentType::SchemaType SchemaType; + typedef typename SchemaDocumentType::PointerType PointerType; typedef typename SchemaType::EncodingType EncodingType; typedef typename EncodingType::Ch Ch; GenericSchemaValidator( const SchemaDocumentType& schemaDocument, StateAllocator* allocator = 0, - size_t schemaStackCapacity = kDefaultSchemaStackCapacity/*, - size_t documentStackCapacity = kDefaultDocumentStackCapacity*/) + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) : + schemaDocument_(&schemaDocument), root_(schemaDocument.GetRoot()), outputHandler_(nullOutputHandler_), schemaStack_(allocator, schemaStackCapacity), - // documentStack_(allocator, documentStackCapacity), + documentStack_(allocator, documentStackCapacity), valid_(true) { } @@ -1181,13 +1192,14 @@ public: const SchemaDocumentType& schemaDocument, OutputHandler& outputHandler, StateAllocator* allocator = 0, - size_t schemaStackCapacity = kDefaultSchemaStackCapacity/*, - size_t documentStackCapacity = kDefaultDocumentStackCapacity*/) + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) : + schemaDocument_(&schemaDocument), root_(schemaDocument.GetRoot()), outputHandler_(outputHandler), schemaStack_(allocator, schemaStackCapacity), - // documentStack_(allocator, documentStackCapacity), + documentStack_(allocator, documentStackCapacity), valid_(true) { } @@ -1206,6 +1218,14 @@ public: // Implementation of ISchemaValidator virtual bool IsValid() const { return valid_; } + PointerType GetInvalidSchemaPointer() const { + return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema()); + } + + PointerType GetInvalidDocumentPointer() const { + return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } + #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ if (!valid_) return false; \ if (!BeginValue() || !CurrentSchema().method arg1) return valid_ = false; @@ -1263,6 +1283,7 @@ public: bool Key(const Ch* str, SizeType len, bool copy) { if (!valid_) return false; + AppendToken(str, len); if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); return valid_ = outputHandler_.Key(str, len, copy); @@ -1303,13 +1324,14 @@ private: GenericSchemaValidator( const SchemaType& root, StateAllocator* allocator = 0, - size_t schemaStackCapacity = kDefaultSchemaStackCapacity/*, - size_t documentStackCapacity = kDefaultDocumentStackCapacity*/) + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) : + schemaDocument_(), root_(root), outputHandler_(nullOutputHandler_), schemaStack_(allocator, schemaStackCapacity), - // documentStack_(allocator, documentStackCapacity), + documentStack_(allocator, documentStackCapacity), valid_(true) { } @@ -1318,6 +1340,9 @@ private: if (schemaStack_.Empty()) PushSchema(root_); else { + if (CurrentContext().inArray) + AppendToken(CurrentContext().arrayElementIndex); + if (!CurrentSchema().BeginValue(CurrentContext())) return false; @@ -1345,6 +1370,12 @@ private: if (!CurrentSchema().EndValue(CurrentContext())) return false; + // *documentStack_.template Push() = '\0'; + // documentStack_.template Pop(1); + // printf("document: %s\n", documentStack_.template Bottom()); + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') + ; + uint64_t h = CurrentContext().arrayUniqueness ? CurrentContext().hasher->GetHashCode() : 0; PopSchema(); @@ -1362,19 +1393,44 @@ private: return true; } + void AppendToken(const Ch* str, SizeType len) { + *documentStack_.template Push() = '/'; + for (SizeType i = 0; i < len; i++) { + if (str[i] == '~') { + *documentStack_.template Push() = '~'; + *documentStack_.template Push() = '0'; + } + else if (str[i] == '/') { + *documentStack_.template Push() = '~'; + *documentStack_.template Push() = '1'; + } + else + *documentStack_.template Push() = str[i]; + } + } + + void AppendToken(SizeType index) { + *documentStack_.template Push() = '/'; + char buffer[21]; + SizeType length = (sizeof(SizeType) == 4 ? internal::u32toa(index, buffer): internal::u64toa(index, buffer)) - buffer; + for (SizeType i = 0; i < length; i++) + *documentStack_.template Push() = buffer[i]; + } + void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(this, &contextAllocator_, &schema); } void PopSchema() { schemaStack_.template Pop(1)->~Context(); } - const SchemaType& CurrentSchema() { return *schemaStack_.template Top()->schema; } + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } Context& CurrentContext() { return *schemaStack_.template Top(); } static const size_t kDefaultSchemaStackCapacity = 1024; - //static const size_t kDefaultDocumentStackCapacity = 256; + static const size_t kDefaultDocumentStackCapacity = 256; + const SchemaDocument* schemaDocument_; const SchemaType& root_; BaseReaderHandler nullOutputHandler_; OutputHandler& outputHandler_; CrtAllocator contextAllocator_; internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) - //internal::Stack documentStack_; //!< stack to store the current path of validating document (Value *) + internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) bool valid_; }; diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index 37e8a17..1346460 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -14,6 +14,7 @@ #include "unittest.h" #include "rapidjson/schema.h" +#include "rapidjson/stringbuffer.h" using namespace rapidjson; @@ -94,11 +95,41 @@ TEST(SchemaValidator, Hasher) { {\ SchemaValidator validator(schema);\ Document d;\ - /*printf("\n%s\n", json);*/\ d.Parse(json);\ EXPECT_FALSE(d.HasParseError());\ EXPECT_TRUE(expected == d.Accept(validator));\ EXPECT_TRUE(expected == validator.IsValid());\ + /*if (!validator.IsValid()) {\ + StringBuffer sb;\ + validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);\ + printf("Error schema: %s\n", sb.GetString());\ + sb.Clear();\ + validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);\ + printf("Error document: %s\n", sb.GetString());\ + }*/\ +} + +#define VALIDATE_ERROR(schema, json, invalidSchemaPointer, invalidDocumentPointer) \ +{\ + SchemaValidator validator(schema);\ + Document d;\ + /*printf("\n%s\n", json);*/\ + d.Parse(json);\ + EXPECT_FALSE(d.HasParseError());\ + EXPECT_FALSE(d.Accept(validator));\ + EXPECT_FALSE(validator.IsValid());\ + if (validator.GetInvalidSchemaPointer() != Pointer(invalidSchemaPointer)) {\ + StringBuffer sb;\ + validator.GetInvalidSchemaPointer().Stringify(sb);\ + printf("GetInvalidSchemaPointer() Expected: %s Actual: %s\n", invalidSchemaPointer, sb.GetString());\ + ADD_FAILURE();\ + }\ + if (validator.GetInvalidDocumentPointer() != Pointer(invalidDocumentPointer)) {\ + StringBuffer sb;\ + validator.GetInvalidDocumentPointer().Stringify(sb);\ + printf("GetInvalidDocumentPointer() Expected: %s Actual: %s\n", invalidDocumentPointer, sb.GetString());\ + ADD_FAILURE();\ + }\ } TEST(SchemaValidator, Typeless) { @@ -118,7 +149,7 @@ TEST(SchemaValidator, MultiType) { VALIDATE(s, "42", true); VALIDATE(s, "\"Life, the universe, and everything\"", true); - VALIDATE(s, "[\"Life\", \"the universe\", \"and everything\"]", false); + VALIDATE_ERROR(s, "[\"Life\", \"the universe\", \"and everything\"]", "", ""); } TEST(SchemaValidator, Enum_Typed) { @@ -153,10 +184,10 @@ TEST(SchemaValidator, Enum_InvalidType) { TEST(SchemaValidator, AllOf) { { Document sd; - sd.Parse("{\"allOf\": [{ \"type\": \"string\" }, { \"type\": \"string\", \"maxLength\": 5 }]}"); // need "type": "string" now + sd.Parse("{\"allOf\": [{ \"type\": \"string\" }, { \"type\": \"string\", \"maxLength\": 5 }]}"); SchemaDocument s(sd); - //VALIDATE(s, "\"ok\"", true); + VALIDATE(s, "\"ok\"", true); VALIDATE(s, "\"too long\"", false); } { @@ -261,7 +292,7 @@ TEST(SchemaValidator, Ref_AllOf) { "}"); SchemaDocument s(sd); - VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"} }", false); + VALIDATE_ERROR(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"} }", "/properties/shipping_address", "/shipping_address"); VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\", \"type\": \"business\"} }", true); }