Add invalid schema/document pointers

This commit is contained in:
Milo Yip 2015-05-11 13:55:34 +08:00
parent 6b7e7d769d
commit 5bc9523cbf
2 changed files with 104 additions and 17 deletions

View File

@ -1035,6 +1035,8 @@ public:
typedef internal::Schema<GenericSchemaDocument> SchemaType; typedef internal::Schema<GenericSchemaDocument> SchemaType;
typedef GenericPointer<ValueType, CrtAllocator> PointerType; typedef GenericPointer<ValueType, CrtAllocator> PointerType;
friend class internal::Schema<GenericSchemaDocument>; friend class internal::Schema<GenericSchemaDocument>;
template <typename, typename, typename>
friend class GenericSchemaValidator;
GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
remoteProvider_(remoteProvider), remoteProvider_(remoteProvider),
@ -1136,6 +1138,13 @@ private:
return 0; return 0;
} }
PointerType GetPointer(const SchemaType* schema) const {
for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
if (schema == target->schema)
return target->pointer;
return PointerType();
}
static const size_t kInitialSchemaMapSize = 64; static const size_t kInitialSchemaMapSize = 64;
static const size_t kInitialSchemaRefSize = 64; static const size_t kInitialSchemaRefSize = 64;
@ -1160,19 +1169,21 @@ class GenericSchemaValidator :
{ {
public: public:
typedef typename SchemaDocumentType::SchemaType SchemaType; typedef typename SchemaDocumentType::SchemaType SchemaType;
typedef typename SchemaDocumentType::PointerType PointerType;
typedef typename SchemaType::EncodingType EncodingType; typedef typename SchemaType::EncodingType EncodingType;
typedef typename EncodingType::Ch Ch; typedef typename EncodingType::Ch Ch;
GenericSchemaValidator( GenericSchemaValidator(
const SchemaDocumentType& schemaDocument, const SchemaDocumentType& schemaDocument,
StateAllocator* allocator = 0, StateAllocator* allocator = 0,
size_t schemaStackCapacity = kDefaultSchemaStackCapacity/*, size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
size_t documentStackCapacity = kDefaultDocumentStackCapacity*/) size_t documentStackCapacity = kDefaultDocumentStackCapacity)
: :
schemaDocument_(&schemaDocument),
root_(schemaDocument.GetRoot()), root_(schemaDocument.GetRoot()),
outputHandler_(nullOutputHandler_), outputHandler_(nullOutputHandler_),
schemaStack_(allocator, schemaStackCapacity), schemaStack_(allocator, schemaStackCapacity),
// documentStack_(allocator, documentStackCapacity), documentStack_(allocator, documentStackCapacity),
valid_(true) valid_(true)
{ {
} }
@ -1181,13 +1192,14 @@ public:
const SchemaDocumentType& schemaDocument, const SchemaDocumentType& schemaDocument,
OutputHandler& outputHandler, OutputHandler& outputHandler,
StateAllocator* allocator = 0, StateAllocator* allocator = 0,
size_t schemaStackCapacity = kDefaultSchemaStackCapacity/*, size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
size_t documentStackCapacity = kDefaultDocumentStackCapacity*/) size_t documentStackCapacity = kDefaultDocumentStackCapacity)
: :
schemaDocument_(&schemaDocument),
root_(schemaDocument.GetRoot()), root_(schemaDocument.GetRoot()),
outputHandler_(outputHandler), outputHandler_(outputHandler),
schemaStack_(allocator, schemaStackCapacity), schemaStack_(allocator, schemaStackCapacity),
// documentStack_(allocator, documentStackCapacity), documentStack_(allocator, documentStackCapacity),
valid_(true) valid_(true)
{ {
} }
@ -1206,6 +1218,14 @@ public:
// Implementation of ISchemaValidator // Implementation of ISchemaValidator
virtual bool IsValid() const { return valid_; } 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<Ch>(), documentStack_.GetSize() / sizeof(Ch));
}
#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
if (!valid_) return false; \ if (!valid_) return false; \
if (!BeginValue() || !CurrentSchema().method arg1) return valid_ = false; if (!BeginValue() || !CurrentSchema().method arg1) return valid_ = false;
@ -1263,6 +1283,7 @@ public:
bool Key(const Ch* str, SizeType len, bool copy) { bool Key(const Ch* str, SizeType len, bool copy) {
if (!valid_) return false; if (!valid_) return false;
AppendToken(str, len);
if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
return valid_ = outputHandler_.Key(str, len, copy); return valid_ = outputHandler_.Key(str, len, copy);
@ -1303,13 +1324,14 @@ private:
GenericSchemaValidator( GenericSchemaValidator(
const SchemaType& root, const SchemaType& root,
StateAllocator* allocator = 0, StateAllocator* allocator = 0,
size_t schemaStackCapacity = kDefaultSchemaStackCapacity/*, size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
size_t documentStackCapacity = kDefaultDocumentStackCapacity*/) size_t documentStackCapacity = kDefaultDocumentStackCapacity)
: :
schemaDocument_(),
root_(root), root_(root),
outputHandler_(nullOutputHandler_), outputHandler_(nullOutputHandler_),
schemaStack_(allocator, schemaStackCapacity), schemaStack_(allocator, schemaStackCapacity),
// documentStack_(allocator, documentStackCapacity), documentStack_(allocator, documentStackCapacity),
valid_(true) valid_(true)
{ {
} }
@ -1318,6 +1340,9 @@ private:
if (schemaStack_.Empty()) if (schemaStack_.Empty())
PushSchema(root_); PushSchema(root_);
else { else {
if (CurrentContext().inArray)
AppendToken(CurrentContext().arrayElementIndex);
if (!CurrentSchema().BeginValue(CurrentContext())) if (!CurrentSchema().BeginValue(CurrentContext()))
return false; return false;
@ -1345,6 +1370,12 @@ private:
if (!CurrentSchema().EndValue(CurrentContext())) if (!CurrentSchema().EndValue(CurrentContext()))
return false; return false;
// *documentStack_.template Push<Ch>() = '\0';
// documentStack_.template Pop<Ch>(1);
// printf("document: %s\n", documentStack_.template Bottom<Ch>());
while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
;
uint64_t h = CurrentContext().arrayUniqueness ? CurrentContext().hasher->GetHashCode() : 0; uint64_t h = CurrentContext().arrayUniqueness ? CurrentContext().hasher->GetHashCode() : 0;
PopSchema(); PopSchema();
@ -1362,19 +1393,44 @@ private:
return true; return true;
} }
void AppendToken(const Ch* str, SizeType len) {
*documentStack_.template Push<Ch>() = '/';
for (SizeType i = 0; i < len; i++) {
if (str[i] == '~') {
*documentStack_.template Push<Ch>() = '~';
*documentStack_.template Push<Ch>() = '0';
}
else if (str[i] == '/') {
*documentStack_.template Push<Ch>() = '~';
*documentStack_.template Push<Ch>() = '1';
}
else
*documentStack_.template Push<Ch>() = str[i];
}
}
void AppendToken(SizeType index) {
*documentStack_.template Push<Ch>() = '/';
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<Ch>() = buffer[i];
}
void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(this, &contextAllocator_, &schema); } void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(this, &contextAllocator_, &schema); }
void PopSchema() { schemaStack_.template Pop<Context>(1)->~Context(); } void PopSchema() { schemaStack_.template Pop<Context>(1)->~Context(); }
const SchemaType& CurrentSchema() { return *schemaStack_.template Top<Context>()->schema; } const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
Context& CurrentContext() { return *schemaStack_.template Top<Context>(); } Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
static const size_t kDefaultSchemaStackCapacity = 1024; static const size_t kDefaultSchemaStackCapacity = 1024;
//static const size_t kDefaultDocumentStackCapacity = 256; static const size_t kDefaultDocumentStackCapacity = 256;
const SchemaDocument* schemaDocument_;
const SchemaType& root_; const SchemaType& root_;
BaseReaderHandler<EncodingType> nullOutputHandler_; BaseReaderHandler<EncodingType> nullOutputHandler_;
OutputHandler& outputHandler_; OutputHandler& outputHandler_;
CrtAllocator contextAllocator_; CrtAllocator contextAllocator_;
internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
//internal::Stack<Allocator> documentStack_; //!< stack to store the current path of validating document (Value *) internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
bool valid_; bool valid_;
}; };

View File

@ -14,6 +14,7 @@
#include "unittest.h" #include "unittest.h"
#include "rapidjson/schema.h" #include "rapidjson/schema.h"
#include "rapidjson/stringbuffer.h"
using namespace rapidjson; using namespace rapidjson;
@ -94,11 +95,41 @@ TEST(SchemaValidator, Hasher) {
{\ {\
SchemaValidator validator(schema);\ SchemaValidator validator(schema);\
Document d;\ Document d;\
/*printf("\n%s\n", json);*/\
d.Parse(json);\ d.Parse(json);\
EXPECT_FALSE(d.HasParseError());\ EXPECT_FALSE(d.HasParseError());\
EXPECT_TRUE(expected == d.Accept(validator));\ EXPECT_TRUE(expected == d.Accept(validator));\
EXPECT_TRUE(expected == validator.IsValid());\ 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) { TEST(SchemaValidator, Typeless) {
@ -118,7 +149,7 @@ TEST(SchemaValidator, MultiType) {
VALIDATE(s, "42", true); VALIDATE(s, "42", true);
VALIDATE(s, "\"Life, the universe, and everything\"", 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) { TEST(SchemaValidator, Enum_Typed) {
@ -153,10 +184,10 @@ TEST(SchemaValidator, Enum_InvalidType) {
TEST(SchemaValidator, AllOf) { TEST(SchemaValidator, AllOf) {
{ {
Document sd; 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); SchemaDocument s(sd);
//VALIDATE(s, "\"ok\"", true); VALIDATE(s, "\"ok\"", true);
VALIDATE(s, "\"too long\"", false); VALIDATE(s, "\"too long\"", false);
} }
{ {
@ -261,7 +292,7 @@ TEST(SchemaValidator, Ref_AllOf) {
"}"); "}");
SchemaDocument s(sd); 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); VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\", \"type\": \"business\"} }", true);
} }