Commit 05968b70 authored by Milo Yip's avatar Milo Yip

Fix schema tests and added SchemaValidatingReader

parent 6978f878
......@@ -1869,6 +1869,21 @@ public:
*/
friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); }
//! Populate this document by a generator which produces SAX events.
/*! \tparam Generator A functor with <tt>bool f(Handler)</tt> prototype.
\param g Generator functor which sends SAX events to the parameter.
\return The document itself for fluent API.
*/
template <typename Generator>
GenericDocument& Populate(Generator& g) {
ClearStackOnExit scope(*this);
if (g(*this)) {
RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object
ValueType::operator=(*stack_.template Pop<ValueType>(1));// Move value from stack to document
}
return *this;
}
//!@name Parse from stream
//!@{
......@@ -2017,9 +2032,10 @@ private:
};
// callers of the following private Handler functions
template <typename,typename,typename> friend class GenericReader; // for parsing
// template <typename,typename,typename> friend class GenericReader; // for parsing
template <typename, typename> friend class GenericValue; // for deep copying
public:
// Implementation of Handler
bool Null() { new (stack_.template Push<ValueType>()) ValueType(); return true; }
bool Bool(bool b) { new (stack_.template Push<ValueType>()) ValueType(b); return true; }
......
......@@ -18,6 +18,12 @@
#include "../rapidjson.h"
#include "stack.h"
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
RAPIDJSON_DIAG_OFF(switch-enum)
#endif
#ifndef RAPIDJSON_REGEX_VERBOSE
#define RAPIDJSON_REGEX_VERBOSE 0
#endif
......@@ -639,4 +645,8 @@ typedef GenericRegex<UTF8<> > Regex;
} // namespace internal
RAPIDJSON_NAMESPACE_END
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_INTERNAL_REGEX_H_
......@@ -19,6 +19,12 @@
#include "pointer.h"
#include <cmath> // HUGE_VAL, abs, floor
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(weak-vtables)
RAPIDJSON_DIAG_OFF(exit-time-destructors)
#endif
#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1
#else
......@@ -56,6 +62,11 @@ RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(variadic-macros)
#endif
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
......@@ -865,39 +876,39 @@ public:
return v;\
}
RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l');
RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n');
RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't');
RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y');
RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g');
RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r');
RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r');
RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e');
RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm');
RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f');
RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f');
RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f');
RAPIDJSON_STRING_(Not, 'n', 'o', 't');
RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's');
RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd');
RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's');
RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's');
RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's');
RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's');
RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's');
RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's');
RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's');
RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's');
RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's');
RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's');
RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h');
RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h');
RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n');
RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm');
RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm');
RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm');
RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm');
RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f');
RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
RAPIDJSON_STRING_(Not, 'n', 'o', 't')
RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
#undef RAPIDJSON_STRING_
......@@ -1380,9 +1391,9 @@ private:
if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i - 1)) {
PointerType pointer(&s[i], len - i, allocator_);
if (pointer.IsValid()) {
if (const SchemaType* s = remoteDocument->GetSchema(pointer)) {
if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
if (schema)
*schema = s;
*schema = sc;
return true;
}
}
......@@ -1414,7 +1425,7 @@ private:
PointerType GetPointer(const SchemaType* schema) const {
for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
if (schema== target->schema)
if (schema == target->schema)
return target->pointer;
return PointerType();
}
......@@ -1457,13 +1468,39 @@ public:
schemaDocument_(&schemaDocument),
root_(schemaDocument.GetRoot()),
outputHandler_(nullOutputHandler_),
stateAllocator_(allocator),
ownStateAllocator_(0),
schemaStack_(allocator, schemaStackCapacity),
documentStack_(allocator, documentStackCapacity),
valid_(true)
#if RAPIDJSON_SCHEMA_VERBOSE
, depth_(0)
#endif
{
CreateOwnAllocator();
}
// Constructor with outputHandler
GenericSchemaValidator(
const SchemaDocumentType& schemaDocument,
OutputHandler& outputHandler,
StateAllocator* allocator = 0,
size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
size_t documentStackCapacity = kDefaultDocumentStackCapacity)
:
schemaDocument_(&schemaDocument),
root_(schemaDocument.GetRoot()),
outputHandler_(outputHandler),
stateAllocator_(allocator),
ownStateAllocator_(0),
schemaStack_(allocator, schemaStackCapacity),
documentStack_(&GetStateAllocator(), documentStackCapacity),
documentStack_(allocator, documentStackCapacity),
valid_(true)
#if RAPIDJSON_SCHEMA_VERBOSE
, depth_(0)
#endif
{
CreateOwnAllocator();
}
~GenericSchemaValidator() {
......@@ -1475,7 +1512,7 @@ public:
PopSchema();
//documentStack_.Clear();
valid_ = true;
};
}
// Implementation of ISchemaValidator
virtual bool IsValid() const { return valid_; }
......@@ -1493,7 +1530,7 @@ public:
}
StateAllocator& GetStateAllocator() {
return schemaStack_.GetAllocator();
return *stateAllocator_;
}
#if RAPIDJSON_SCHEMA_VERBOSE
......@@ -1642,6 +1679,8 @@ private:
schemaDocument_(&schemaDocument),
root_(root),
outputHandler_(nullOutputHandler_),
stateAllocator_(allocator),
ownStateAllocator_(0),
schemaStack_(allocator, schemaStackCapacity),
documentStack_(allocator, documentStackCapacity),
valid_(true)
......@@ -1649,6 +1688,12 @@ private:
, depth_(depth)
#endif
{
CreateOwnAllocator();
}
void CreateOwnAllocator() {
if (!stateAllocator_)
stateAllocator_ = ownStateAllocator_ = new StateAllocator;
}
bool BeginValue() {
......@@ -1738,8 +1783,8 @@ private:
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++)
size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer)) - buffer);
for (size_t i = 0; i < length; i++)
*documentStack_.template Push<Ch>() = buffer[i];
}
......@@ -1762,8 +1807,10 @@ private:
static const size_t kDefaultDocumentStackCapacity = 256;
const SchemaDocumentType* schemaDocument_;
const SchemaType& root_;
BaseReaderHandler<EncodingType> nullOutputHandler_;
OutputHandler nullOutputHandler_;
OutputHandler& outputHandler_;
StateAllocator* stateAllocator_;
StateAllocator* ownStateAllocator_;
internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
bool valid_;
......@@ -1774,10 +1821,62 @@ private:
typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
template <
unsigned parseFlags,
typename InputStream,
typename SourceEncoding,
typename SchemaDocumentType = SchemaDocument,
typename StackAllocator = CrtAllocator>
class SchemaValidatingReader {
public:
typedef typename SchemaDocumentType::PointerType PointerType;
typedef typename InputStream::Ch Ch;
SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_() {}
template <typename Handler>
bool operator()(Handler& handler) {
GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader;
GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler);
parseResult_ = reader.template Parse<parseFlags>(is_, validator);
if (validator.IsValid()) {
invalidSchemaPointer_ = PointerType();
invalidSchemaKeyword_ = 0;
invalidDocumentPointer_ = PointerType();
}
else {
invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
}
return parseResult_;
}
const ParseResult& GetParseResult() const { return parseResult_; }
const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
private:
InputStream& is_;
const SchemaDocumentType& sd_;
ParseResult parseResult_;
PointerType invalidSchemaPointer_;
const Ch* invalidSchemaKeyword_;
PointerType invalidDocumentPointer_;
};
RAPIDJSON_NAMESPACE_END
#if defined(__GNUC__)
RAPIDJSON_DIAG_POP
#endif
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_SCHEMA_H_
......@@ -16,6 +16,11 @@
#include "rapidjson/schema.h"
#include "rapidjson/stringbuffer.h"
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(variadic-macros)
#endif
using namespace rapidjson;
#define TEST_HASHER(json1, json2, expected) \
......@@ -95,7 +100,7 @@ TEST(SchemaValidator, Hasher) {
{\
SchemaValidator validator(schema);\
Document d;\
printf("\n%s\n", json);\
/*printf("\n%s\n", json);*/\
d.Parse(json);\
EXPECT_FALSE(d.HasParseError());\
EXPECT_TRUE(expected == d.Accept(validator));\
......@@ -115,7 +120,7 @@ TEST(SchemaValidator, Hasher) {
{\
SchemaValidator validator(schema);\
Document d;\
printf("\n%s\n", json);\
/*printf("\n%s\n", json);*/\
d.Parse(json);\
EXPECT_FALSE(d.HasParseError());\
EXPECT_FALSE(d.Accept(validator));\
......@@ -841,16 +846,16 @@ TEST(SchemaValidator, AllOf_Nested) {
template <typename Allocator>
static char* ReadFile(const char* filename, Allocator& allocator) {
const char *paths[] = {
"%s",
"bin/%s",
"../bin/%s",
"../../bin/%s",
"../../../bin/%s"
"",
"bin/",
"../bin/",
"../../bin/",
"../../../bin/"
};
char buffer[1024];
FILE *fp = 0;
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
sprintf(buffer, paths[i], filename);
sprintf(buffer, "%s%s", paths[i], filename);
fp = fopen(buffer, "rb");
if (fp)
break;
......@@ -860,9 +865,9 @@ static char* ReadFile(const char* filename, Allocator& allocator) {
return 0;
fseek(fp, 0, SEEK_END);
size_t length = (size_t)ftell(fp);
size_t length = static_cast<size_t>(ftell(fp));
fseek(fp, 0, SEEK_SET);
char* json = (char*)allocator.Malloc(length + 1);
char* json = reinterpret_cast<char*>(allocator.Malloc(length + 1));
size_t readLength = fread(json, 1, length, fp);
json[readLength] = '\0';
fclose(fp);
......@@ -1088,3 +1093,38 @@ TEST(SchemaValidator, TestSuite) {
// if (passCount != testCount)
// ADD_FAILURE();
}
TEST(SchemaValidatingReader, Valid) {
Document sd;
sd.Parse("{ \"type\": \"string\", \"enum\" : [\"red\", \"amber\", \"green\"] }");
SchemaDocument s(sd);
Document d;
StringStream ss("\"red\"");
SchemaValidatingReader<kParseDefaultFlags, StringStream, UTF8<> > reader(ss, s);
d.Populate(reader);
EXPECT_TRUE(reader.GetParseResult());
EXPECT_TRUE(d.IsString());
EXPECT_STREQ("red", d.GetString());
}
TEST(SchemaValidatingReader, Invalid) {
Document sd;
sd.Parse("{\"type\":\"string\",\"minLength\":2,\"maxLength\":3}");
SchemaDocument s(sd);
Document d;
StringStream ss("\"ABCD\"");
SchemaValidatingReader<kParseDefaultFlags, StringStream, UTF8<> > reader(ss, s);
d.Populate(reader);
EXPECT_FALSE(reader.GetParseResult());
EXPECT_EQ(kParseErrorTermination, reader.GetParseResult().Code());
EXPECT_STREQ("maxLength", reader.GetInvalidSchemaKeyword());
EXPECT_TRUE(reader.GetInvalidSchemaPointer() == SchemaDocument::PointerType(""));
EXPECT_TRUE(reader.GetInvalidDocumentPointer() == SchemaDocument::PointerType(""));
EXPECT_TRUE(d.IsNull());
}
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
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