Commit 5bc9523c authored by Milo Yip's avatar Milo Yip

Add invalid schema/document pointers

parent 6b7e7d76
......@@ -1035,6 +1035,8 @@ public:
typedef internal::Schema<GenericSchemaDocument> SchemaType;
typedef GenericPointer<ValueType, CrtAllocator> PointerType;
friend class internal::Schema<GenericSchemaDocument>;
template <typename, typename, typename>
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<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 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<Ch>(), 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<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;
PopSchema();
......@@ -1362,19 +1393,44 @@ private:
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 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>(); }
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<EncodingType> nullOutputHandler_;
OutputHandler& outputHandler_;
CrtAllocator contextAllocator_;
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_;
};
......
......@@ -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);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment