diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 12187ea..59cdca1 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -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 bool f(Handler) prototype. + \param g Generator functor which sends SAX events to the parameter. + \return The document itself for fluent API. + */ + template + 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(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 friend class GenericReader; // for parsing + // template friend class GenericReader; // for parsing template friend class GenericValue; // for deep copying +public: // Implementation of Handler bool Null() { new (stack_.template Push()) ValueType(); return true; } bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } diff --git a/include/rapidjson/internal/regex.h b/include/rapidjson/internal/regex.h index f3333b2..5c82fb4 100644 --- a/include/rapidjson/internal/regex.h +++ b/include/rapidjson/internal/regex.h @@ -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 > Regex; } // namespace internal RAPIDJSON_NAMESPACE_END +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + #endif // RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index fda3bf6..6bb3a35 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -19,6 +19,12 @@ #include "pointer.h" #include // 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(); target != schemaMap_.template End(); ++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_(&GetStateAllocator(), documentStackCapacity), + 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_(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() = '/'; 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((sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer)) - buffer); + for (size_t i = 0; i < length; i++) *documentStack_.template Push() = buffer[i]; } @@ -1762,8 +1807,10 @@ private: static const size_t kDefaultDocumentStackCapacity = 256; const SchemaDocumentType* schemaDocument_; const SchemaType& root_; - BaseReaderHandler nullOutputHandler_; + OutputHandler nullOutputHandler_; OutputHandler& outputHandler_; + StateAllocator* stateAllocator_; + StateAllocator* ownStateAllocator_; internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) bool valid_; @@ -1774,10 +1821,62 @@ private: typedef GenericSchemaValidator 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 + bool operator()(Handler& handler) { + GenericReader reader; + GenericSchemaValidator validator(sd_, handler); + parseResult_ = reader.template Parse(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_ diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index e742a12..95b5bb0 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -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 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(ftell(fp)); fseek(fp, 0, SEEK_SET); - char* json = (char*)allocator.Malloc(length + 1); + char* json = reinterpret_cast(allocator.Malloc(length + 1)); size_t readLength = fread(json, 1, length, fp); json[readLength] = '\0'; fclose(fp); @@ -1087,4 +1092,39 @@ TEST(SchemaValidator, TestSuite) { printf("%d / %d passed (%2d%%)\n", passCount, testCount, passCount * 100 / testCount); // if (passCount != testCount) // ADD_FAILURE(); -} \ No newline at end of file +} + +TEST(SchemaValidatingReader, Valid) { + Document sd; + sd.Parse("{ \"type\": \"string\", \"enum\" : [\"red\", \"amber\", \"green\"] }"); + SchemaDocument s(sd); + + Document d; + StringStream ss("\"red\""); + SchemaValidatingReader > 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 > 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 diff --git a/test/unittest/strfunctest.cpp b/test/unittest/strfunctest.cpp index 3e1a1ce..186755c 100644 --- a/test/unittest/strfunctest.cpp +++ b/test/unittest/strfunctest.cpp @@ -28,4 +28,4 @@ TEST(StrFunc, CountStringCodePoint) { EXPECT_TRUE(CountStringCodePoint >("\xC2\xA2\xE2\x82\xAC\xF0\x9D\x84\x9E", 9, &count)); // cents euro G-clef EXPECT_EQ(3u, count); EXPECT_FALSE(CountStringCodePoint >("\xC2\xA2\xE2\x82\xAC\xF0\x9D\x84\x9E\x80", 10, &count)); -} \ No newline at end of file +}