diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 03fc37c..8fc3fcd 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -18,6 +18,20 @@ #include "document.h" #include // HUGE_VAL, fmod +#if !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && __cplusplus >=201103L +#define RAPIDJSON_SCHEMA_USE_STDREGEX 1 +#endif + +#if RAPIDJSON_SCHEMA_USE_STDREGEX +#include +#endif + +#if RAPIDJSON_SCHEMA_USE_STDREGEX // or some other implementation +#define RAPIDJSON_SCHEMA_HAS_REGEX 1 +#else +#define RAPIDJSON_SCHEMA_HAS_REGEX 0 +#endif + #if defined(__GNUC__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) @@ -602,6 +616,9 @@ public: template StringSchema(const ValueType& value) : BaseSchema(value), +#if RAPIDJSON_SCHEMA_USE_STDREGEX + pattern_(), +#endif minLength_(0), maxLength_(~SizeType(0)) { @@ -622,6 +639,34 @@ public: // Error } } + +#if RAPIDJSON_SCHEMA_HAS_REGEX + typename ValueType::ConstMemberIterator patternItr = value.FindMember("pattern"); + if (patternItr != value.MemberEnd()) { + if (patternItr->value.IsString()) { +#if RAPIDJSON_SCHEMA_USE_STDREGEX + try { + pattern_ = new std::basic_regex( + patternItr->value.GetString(), + std::size_t(patternItr->value.GetStringLength()), + std::regex_constants::ECMAScript); + } + catch (const std::regex_error&) { + // Error + } +#endif // RAPIDJSON_SCHEMA_USE_STDREGEX + } + else { + // Error + } + } +#endif // RAPIDJSON_SCHEMA_HAS_REGEX + } + + ~StringSchema() { +#if RAPIDJSON_SCHEMA_USE_STDREGEX + delete pattern_; +#endif } virtual SchemaType GetSchemaType() const { return kStringSchemaType; } @@ -635,7 +680,22 @@ public: virtual bool Double(double) const { return false; } virtual bool String(const Ch* str, SizeType length, bool copy) const { - return BaseSchema::String(str, length, copy) && length >= minLength_ && length <= maxLength_; + if (!BaseSchema::String(str, length, copy)) + return false; + if (length < minLength_ || length > maxLength_) + return false; + +#if RAPIDJSON_SCHEMA_HAS_REGEX + if (pattern_) { +#if RAPIDJSON_SCHEMA_USE_STDREGEX + std::match_results r; + if (!std::regex_match(str, str + length, r, *pattern_)) + return false; +#endif // RAPIDJSON_SCHEMA_USE_STDREGEX + } +#endif // RAPIDJSON_SCHEMA_HAS_REGEX + + return true; } virtual bool StartObject(Context&) const { return false; } @@ -645,6 +705,9 @@ public: virtual bool EndArray(Context&, SizeType) const { return true; } private: +#if RAPIDJSON_SCHEMA_USE_STDREGEX + std::basic_regex* pattern_; +#endif SizeType minLength_; SizeType maxLength_; }; diff --git a/test/unittest/CMakeLists.txt b/test/unittest/CMakeLists.txt index cd69a76..7a3db6a 100644 --- a/test/unittest/CMakeLists.txt +++ b/test/unittest/CMakeLists.txt @@ -20,9 +20,9 @@ set(UNITTEST_SOURCES writertest.cpp) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal") elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Werror -Wall -Wextra -Weffc++ -Wswitch-default -Wfloat-equal") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") add_definitions(-D_CRT_SECURE_NO_WARNINGS=1) endif() diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index 2d9476a..293ea9e 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -102,6 +102,19 @@ TEST(SchemaValidator, String_LengthRange) { VALIDATE(s, "\"ABCD\"", false); } +#if RAPIDJSON_SCHEMA_HAS_REGEX +TEST(SchemaValidator, String_Pattern) { + Document sd; + sd.Parse("{\"type\":\"string\",\"pattern\":\"^(\\\\([0-9]{3}\\\\))?[0-9]{3}-[0-9]{4}$\"}"); + Schema s(sd); + + VALIDATE(s, "\"555-1212\"", true); + VALIDATE(s, "\"(888)555-1212\"", true); + VALIDATE(s, "\"(888)555-1212 ext. 532\"", false); + VALIDATE(s, "\"(800)FLOWERS\"", false); +} +#endif + TEST(SchemaValidator, Integer) { Document sd; sd.Parse("{\"type\":\"integer\"}");