diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 002d760..f42f9c9 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -903,6 +903,7 @@ public: friend class internal::Schema; GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : + document_(document), remoteProvider_(remoteProvider), root_(), schemaMap_(allocator, kInitialSchemaMapSize), @@ -918,22 +919,8 @@ public: SchemaEntry* refEntry = schemaRef_.template Pop(1); PointerType p = refEntry->pointer; // Due to re-entrance, SchemaType* source = refEntry->schema; // backup the entry first, - refEntry->~SchemaEntry(); // and then destruct it. - - bool resolved = false; - for (SchemaEntry* target = schemaMap_.template Bottom(); target <= schemaMap_.template Top(); ++target) - if (p == target->pointer) { - source->ref_ = target->schema; - resolved = true; - break; - } - - // If not reesolved to existing schemas, try to create schema from the pointer - if (!resolved) { - if (const ValueType* v = p.Get(document)) - source->ref_ = CreateSchema(p, *v); // cause re-entrance (modifying schemaRef_) - } - + refEntry->~SchemaEntry(); // and then destruct it. + source->ref_ = GetSchema(p); } } @@ -970,32 +957,41 @@ private: while (i < len && s[i] != '#') // Find the first # i++; - if (s[i] == '#') { - if (i > 0) { // Remote reference, resolve immediately - if (remoteProvider_) { - GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i); - GenericPointer pointer(s, len - i); - schema->ref_ = remoteDocument->GetSchema(pointer); + if (i > 0) { // Remote reference, resolve immediately + if (remoteProvider_) { + if (GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i - 1)) { + printf("remote fragment: %*s\n", len - i, &s[i]); + GenericPointer pointer(&s[i], len - i); + if (pointer.IsValid()) + schema->ref_ = remoteDocument->GetSchema(pointer); } } - else { // Local reference, defer resolution - GenericPointer pointer(v.GetString(), v.GetStringLength()); - if (pointer.IsValid()) - new (schemaRef_.template Push()) SchemaEntry(pointer, schema); - } + } + else if (s[i] == '#') { // Local reference, defer resolution + printf("local fragment: %*s\n", len - i, &s[i]); + GenericPointer pointer(&s[i], len - i); + if (pointer.IsValid()) + new (schemaRef_.template Push()) SchemaEntry(pointer, schema); } } } } const SchemaType* GetSchema(const GenericPointer& pointer) { - (void)pointer; - return 0; + for (SchemaEntry* target = schemaMap_.template Bottom(); target <= schemaMap_.template Top(); ++target) + if (pointer == target->pointer) + return target->schema; + + if (const ValueType* v = pointer.Get(document_)) + return CreateSchema(pointer, *v); + else + return 0; } static const size_t kInitialSchemaMapSize = 1024; static const size_t kInitialSchemaRefSize = 1024; + const ValueType& document_; IRemoteSchemaDocumentProviderType* remoteProvider_; const SchemaType* root_; //!< Root schema. internal::Stack schemaMap_; // Stores created Pointer -> Schemas diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index 3371c1d..5e318b2 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -698,11 +698,11 @@ TEST(SchemaValidator, AllOf_Nested) { static char* ReadFile(const char* filename, size_t& length) { const char *paths[] = { - "jsonschema/tests/draft4/%s", - "bin/jsonschema/tests/draft4/%s", - "../bin/jsonschema/tests/draft4/%s", - "../../bin/jsonschema/tests/draft4/%s", - "../../../bin/jsonschema/tests/draft4/%s" + "%s", + "bin/%s", + "../bin/%s", + "../../bin/%s", + "../../../bin/%s" }; char buffer[1024]; FILE *fp = 0; @@ -726,6 +726,62 @@ static char* ReadFile(const char* filename, size_t& length) { return json; } +class RemoteSchemaDocumentProvider : public IRemoteSchemaDocumentProvider { +public: + RemoteSchemaDocumentProvider() { + const char* filenames[kCount] = { + "integer.json", + "subSchemas.json", + "folder/folderInteger.json" + }; + + for (size_t i = 0; i < kCount; i++) { + d_[i] = 0; + sd_[i] = 0; + + char filename[FILENAME_MAX]; + sprintf(filename, "jsonschema/remotes/%s", filenames[i]); + size_t length; + char* json = ReadFile(filename, length); + if (!json) { + printf("json remote file %s not found", filename); + ADD_FAILURE(); + } + else { + d_[i] = new Document; + d_[i]->Parse(json); + sd_[i] = new SchemaDocument(*d_[i]); + free(json); + } + }; + } + + ~RemoteSchemaDocumentProvider() { + for (size_t i = 0; i < kCount; i++) { + delete d_[i]; + delete sd_[i]; + } + } + + virtual SchemaDocument* GetRemoteDocument(const char* uri, SizeType length) { + const char* uris[kCount] = { + "http://localhost:1234/integer.json", + "http://localhost:1234/subSchemas.json", + "http://localhost:1234/folder/folderInteger.json" + }; + + for (size_t i = 0; i < kCount; i++) { + if (strncmp(uri, uris[i], length) == 0) + return sd_[i]; + } + return 0; + } + +private: + static const size_t kCount = 3; + Document* d_[kCount]; + SchemaDocument* sd_[kCount]; +}; TEST(SchemaValidator, TestSuite) { const char* filenames[] = { @@ -765,8 +821,11 @@ TEST(SchemaValidator, TestSuite) { unsigned testCount = 0; unsigned passCount = 0; + RemoteSchemaDocumentProvider provider; + for (size_t i = 0; i < sizeof(filenames) / sizeof(filenames[0]); i++) { - const char* filename = filenames[i]; + char filename[FILENAME_MAX]; + sprintf(filename, "jsonschema/tests/draft4/%s", filenames[i]); size_t length; char* json = ReadFile(filename, length); if (!json) { @@ -782,7 +841,7 @@ TEST(SchemaValidator, TestSuite) { } else { for (Value::ConstValueIterator schemaItr = d.Begin(); schemaItr != d.End(); ++schemaItr) { - SchemaDocument schema((*schemaItr)["schema"]); + SchemaDocument schema((*schemaItr)["schema"], &provider); SchemaValidator validator(schema); const char* description1 = (*schemaItr)["description"].GetString(); const Value& tests = (*schemaItr)["tests"];