From a92c3b6995553709a5c23a464e7da8078094b18c Mon Sep 17 00:00:00 2001 From: miloyip Date: Sun, 17 May 2015 10:21:58 +0800 Subject: [PATCH] Make schema working for UTF-16 and other encodings --- include/rapidjson/schema.h | 292 ++++++++++++++++++++++------------- test/unittest/schematest.cpp | 32 +++- 2 files changed, 210 insertions(+), 114 deletions(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index fd9ba05..f24257d 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -48,9 +48,48 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Verbose Utilities + #if RAPIDJSON_SCHEMA_VERBOSE -#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) \ - printf("Fail keyword: %s\n", keyword) + +namespace internal { + +inline void PrintInvalidKeyword(const char* keyword) { + printf("Fail keyword: %s\n", keyword); +} + +inline void PrintInvalidKeyword(const wchar_t* keyword) { + wprintf(L"Fail keyword: %ls\n", keyword); +} + +inline void PrintInvalidDocument(const char* document) { + printf("Fail document: %s\n\n", document); +} + +inline void PrintInvalidDocument(const wchar_t* document) { + wprintf(L"Fail document: %ls\n\n", document); +} + +inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) { + printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d); +} + +inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) { + wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d); +} + +} // namespace internal + +#endif // RAPIDJSON_SCHEMA_VERBOSE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_INVALID_KEYWORD_RETURN + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) #else #define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) #endif @@ -62,8 +101,6 @@ RAPIDJSON_MULTILINEMACRO_BEGIN\ return false;\ RAPIDJSON_MULTILINEMACRO_END -RAPIDJSON_NAMESPACE_BEGIN - /////////////////////////////////////////////////////////////////////////////// // Forward declarations @@ -195,6 +232,7 @@ struct SchemaValidationContext { typedef ISchemaValidatorFactory SchemaValidatorFactoryType; typedef GenericValue, CrtAllocator> HashCodeArray; typedef typename SchemaType::ValueType ValueType; + typedef typename ValueType::Ch Ch; typedef Hasher HasherType; enum PatternValidatorType { @@ -244,7 +282,7 @@ struct SchemaValidationContext { CrtAllocator* allocator; // For allocating memory for context const SchemaType* schema; const SchemaType* valueSchema; - const char* invalidKeyword; + const Ch* invalidKeyword; HasherType* hasher; SchemaValidatorArray allOfValidators; SchemaValidatorArray anyOfValidators; @@ -320,7 +358,7 @@ public: if (!value.IsObject()) return; - if (const ValueType* v = GetMember(value, "type")) { + if (const ValueType* v = GetMember(value, GetTypeString())) { type_ = 0; if (v->IsString()) AddType(*v); @@ -329,7 +367,7 @@ public: AddType(*itr); } - if (const ValueType* v = GetMember(value, "enum")) + if (const ValueType* v = GetMember(value, GetEnumString())) if (v->IsArray() && v->Size() > 0) { enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { @@ -339,21 +377,18 @@ public: } } - AssigIfExist(allOf_, document, p, value, "allOf"); - AssigIfExist(anyOf_, document, p, value, "anyOf"); - AssigIfExist(oneOf_, document, p, value, "oneOf"); + AssignIfExist(allOf_, document, p, value, GetAllOfString()); + AssignIfExist(anyOf_, document, p, value, GetAnyOfString()); + AssignIfExist(oneOf_, document, p, value, GetOneOfString()); - if (const ValueType* v = GetMember(value, "not")) - document->CreateSchema(¬_, p.Append("not"), *v); - - //if (const ValueType* v = GetMember(value, "$ref")) - // document->AddRefSchema(this, *v); + if (const ValueType* v = GetMember(value, GetNotString())) + document->CreateSchema(¬_, p.Append(GetNotString()), *v); // Object - const ValueType* properties = GetMember(value, "properties"); - const ValueType* required = GetMember(value, "required"); - const ValueType* dependencies = GetMember(value, "dependencies"); + const ValueType* properties = GetMember(value, GetPropertiesString()); + const ValueType* required = GetMember(value, GetRequiredString()); + const ValueType* dependencies = GetMember(value, GetDependenciesString()); { // Gather properties from properties/required/dependencies SValue allProperties(kArrayType); @@ -388,7 +423,7 @@ public: } if (properties && properties->IsObject()) { - PointerType q = p.Append("properties"); + PointerType q = p.Append(GetPropertiesString()); for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { SizeType index; if (FindPropertyIndex(itr->name, &index)) @@ -396,8 +431,8 @@ public: } } - if (const ValueType* v = GetMember(value, "patternProperties")) { - PointerType q = p.Append("patternProperties"); + if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { + PointerType q = p.Append(GetPatternPropertiesString()); patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); patternPropertyCount_ = 0; @@ -420,7 +455,7 @@ public: } if (dependencies && dependencies->IsObject()) { - PointerType q = p.Append("dependencies"); + PointerType q = p.Append(GetDependenciesString()); hasDependencies_ = true; for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { SizeType sourceIndex; @@ -442,19 +477,19 @@ public: } } - if (const ValueType* v = GetMember(value, "additionalProperties")) { + if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { if (v->IsBool()) additionalProperties_ = v->GetBool(); else if (v->IsObject()) - document->CreateSchema(&additionalPropertiesSchema_, p.Append("additionalProperties"), *v); + document->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString()), *v); } - AssignIfExist(minProperties_, value, "minProperties"); - AssignIfExist(maxProperties_, value, "maxProperties"); + AssignIfExist(minProperties_, value, GetMinPropertiesString()); + AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); // Array - if (const ValueType* v = GetMember(value, "items")) { - PointerType q = p.Append("items"); + if (const ValueType* v = GetMember(value, GetItemsString())) { + PointerType q = p.Append(GetItemsString()); if (v->IsObject()) // List validation document->CreateSchema(&itemsList_, q, *v); else if (v->IsArray()) { // Tuple validation @@ -465,38 +500,38 @@ public: } } - AssignIfExist(minItems_, value, "minItems"); - AssignIfExist(maxItems_, value, "maxItems"); + AssignIfExist(minItems_, value, GetMinItemsString()); + AssignIfExist(maxItems_, value, GetMaxItemsString()); - if (const ValueType* v = GetMember(value, "additionalItems")) { + if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { if (v->IsBool()) additionalItems_ = v->GetBool(); else if (v->IsObject()) - document->CreateSchema(&additionalItemsSchema_, p.Append("additionalItems"), *v); + document->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString()), *v); } - AssignIfExist(uniqueItems_, value, "uniqueItems"); + AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); // String - AssignIfExist(minLength_, value, "minLength"); - AssignIfExist(maxLength_, value, "maxLength"); + AssignIfExist(minLength_, value, GetMinLengthString()); + AssignIfExist(maxLength_, value, GetMaxLengthString()); - if (const ValueType* v = GetMember(value, "pattern")) + if (const ValueType* v = GetMember(value, GetPatternString())) pattern_ = CreatePattern(*v); // Number - if (const ValueType* v = GetMember(value, "minimum")) + if (const ValueType* v = GetMember(value, GetMinimumString())) if (v->IsNumber()) minimum_.CopyFrom(*v, *allocator_); - if (const ValueType* v = GetMember(value, "maximum")) + if (const ValueType* v = GetMember(value, GetMaximumString())) if (v->IsNumber()) maximum_.CopyFrom(*v, *allocator_); - AssignIfExist(exclusiveMinimum_, value, "exclusiveMinimum"); - AssignIfExist(exclusiveMaximum_, value, "exclusiveMaximum"); + AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); + AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); - if (const ValueType* v = GetMember(value, "multipleOf")) + if (const ValueType* v = GetMember(value, GetMultipleOfString())) if (v->IsNumber() && v->GetDouble() > 0.0) multipleOf_.CopyFrom(*v, *allocator_); } @@ -537,7 +572,7 @@ public: else if (additionalItems_) context.valueSchema = GetTypeless(); else - RAPIDJSON_INVALID_KEYWORD_RETURN("items"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); } else context.valueSchema = GetTypeless(); @@ -563,14 +598,14 @@ public: if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { if (!patternValid) - RAPIDJSON_INVALID_KEYWORD_RETURN("patternProperties"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); } else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { if (!patternValid || !otherValid) - RAPIDJSON_INVALID_KEYWORD_RETURN("patternProperties"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); } else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty) - RAPIDJSON_INVALID_KEYWORD_RETURN("patternProperties"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); } if (enum_) { @@ -578,20 +613,20 @@ public: for (SizeType i = 0; i < enumCount_; i++) if (enum_[i] == h) goto foundEnum; - RAPIDJSON_INVALID_KEYWORD_RETURN("enum"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString()); foundEnum:; } if (allOf_.schemas) for (SizeType i = 0; i < allOf_.count; i++) if (!context.allOfValidators.validators[i]->IsValid()) - RAPIDJSON_INVALID_KEYWORD_RETURN("allOf"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString()); if (anyOf_.schemas) { for (SizeType i = 0; i < anyOf_.count; i++) if (context.anyOfValidators.validators[i]->IsValid()) goto foundAny; - RAPIDJSON_INVALID_KEYWORD_RETURN("anyOf"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString()); foundAny:; } @@ -600,29 +635,29 @@ public: for (SizeType i = 0; i < oneOf_.count; i++) if (context.oneOfValidators.validators[i]->IsValid()) { if (oneValid) - RAPIDJSON_INVALID_KEYWORD_RETURN("oneOf"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); else oneValid = true; } if (!oneValid) - RAPIDJSON_INVALID_KEYWORD_RETURN("oneOf"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); } if (not_ && context.notValidator->IsValid()) - RAPIDJSON_INVALID_KEYWORD_RETURN("not"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); return true; } bool Null(Context& context) const { if (!(type_ & (1 << kNullSchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN("type"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); return CreateParallelValidator(context); } bool Bool(Context& context, bool) const { if (!(type_ & (1 << kBooleanSchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN("type"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); return CreateParallelValidator(context); } @@ -652,7 +687,7 @@ public: bool Double(Context& context, double d) const { if (!(type_ & (1 << kNumberSchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN("type"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) return false; @@ -668,27 +703,27 @@ public: bool String(Context& context, const Ch* str, SizeType length, bool) const { if (!(type_ & (1 << kStringSchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN("type"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); if (minLength_ != 0 || maxLength_ != SizeType(~0)) { SizeType count; if (internal::CountStringCodePoint(str, length, &count)) { if (count < minLength_) - RAPIDJSON_INVALID_KEYWORD_RETURN("minLength"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); if (count > maxLength_) - RAPIDJSON_INVALID_KEYWORD_RETURN("maxLength"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); } } if (pattern_ && !IsPatternMatch(pattern_, str, length)) - RAPIDJSON_INVALID_KEYWORD_RETURN("pattern"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); return CreateParallelValidator(context); } bool StartObject(Context& context) const { if (!(type_ & (1 << kObjectSchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN("type"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); context.objectRequiredCount = 0; if (hasDependencies_) { @@ -715,7 +750,7 @@ public: } SizeType index; - if (FindPropertyIndex(str, len, &index)) { + if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { if (context.patternPropertiesSchemaCount > 0) { context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; context.valueSchema = GetTypeless(); @@ -749,20 +784,20 @@ public: } if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties - RAPIDJSON_INVALID_KEYWORD_RETURN("additionalProperties"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); return true; } bool EndObject(Context& context, SizeType memberCount) const { if (context.objectRequiredCount != requiredCount_) - RAPIDJSON_INVALID_KEYWORD_RETURN("required"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); if (memberCount < minProperties_) - RAPIDJSON_INVALID_KEYWORD_RETURN("minProperties"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); if (memberCount > maxProperties_) - RAPIDJSON_INVALID_KEYWORD_RETURN("maxProperties"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); if (hasDependencies_) { for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) @@ -770,11 +805,11 @@ public: if (properties_[sourceIndex].dependencies) { for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) if (properties_[sourceIndex].dependencies[targetIndex] && !context.objectDependencies[targetIndex]) - RAPIDJSON_INVALID_KEYWORD_RETURN("dependencies"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); } else if (properties_[sourceIndex].dependenciesSchema) if (!context.dependencyValidators.validators[sourceIndex]->IsValid()) - RAPIDJSON_INVALID_KEYWORD_RETURN("dependencies"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); } } @@ -783,7 +818,7 @@ public: bool StartArray(Context& context) const { if (!(type_ & (1 << kArraySchemaType))) - RAPIDJSON_INVALID_KEYWORD_RETURN("type"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); if (uniqueItems_) context.arrayElementHashCodes.SetArray(); @@ -798,14 +833,57 @@ public: context.inArray = false; if (elementCount < minItems_) - RAPIDJSON_INVALID_KEYWORD_RETURN("minItems"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); if (elementCount > maxItems_) - RAPIDJSON_INVALID_KEYWORD_RETURN("maxItems"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); return true; } + // Generate functions for string literal according to Ch +#define RAPIDJSON_STRING_(name, ...) \ + static const Ch* Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + return s;\ + } + + 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_ + private: enum SchemaValueType { kNullSchemaType, @@ -832,7 +910,7 @@ private: }; static const SchemaType* GetTypeless() { - static SchemaType typeless(0, PointerType(), Value(kObjectType).Move(), 0); + static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), 0); return &typeless; } @@ -846,27 +924,27 @@ private: } template - static const ValueType* GetMember(const ValueType& value, const char* name) { + static const ValueType* GetMember(const ValueType& value, const Ch* name) { typename ValueType::ConstMemberIterator itr = value.FindMember(name); return itr != value.MemberEnd() ? &(itr->value) : 0; } template - static void AssignIfExist(bool& out, const ValueType& value, const char* name) { + static void AssignIfExist(bool& out, const ValueType& value, const Ch* name) { if (const ValueType* v = GetMember(value, name)) if (v->IsBool()) out = v->GetBool(); } template - static void AssignIfExist(SizeType& out, const ValueType& value, const char* name) { + static void AssignIfExist(SizeType& out, const ValueType& value, const Ch* name) { if (const ValueType* v = GetMember(value, name)) if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) out = static_cast(v->GetUint64()); } template - void AssigIfExist(SchemaArray& out, const DocumentType& document, const PointerType& p, const ValueType& value, const char* name) { + void AssignIfExist(SchemaArray& out, const DocumentType& document, const PointerType& p, const ValueType& value, const Ch* name) { if (const ValueType* v = GetMember(value, name)) { if (v->IsArray() && v->Size() > 0) { PointerType q = p.Append(name); @@ -902,14 +980,14 @@ private: static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } #endif // RAPIDJSON_SCHEMA_USE_STDREGEX - void AddType(const Value& type) { - if (type == "null" ) type_ |= 1 << kNullSchemaType; - else if (type == "boolean") type_ |= 1 << kBooleanSchemaType; - else if (type == "object" ) type_ |= 1 << kObjectSchemaType; - else if (type == "array" ) type_ |= 1 << kArraySchemaType; - else if (type == "string" ) type_ |= 1 << kStringSchemaType; - else if (type == "integer") type_ |= 1 << kIntegerSchemaType; - else if (type == "number" ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); + void AddType(const ValueType& type) { + if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; + else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; + else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; + else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; + else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; + else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; + else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); } bool CreateParallelValidator(Context& context) const { @@ -957,24 +1035,14 @@ private: return false; } - // O(n) - bool FindPropertyIndex(const Ch* str, SizeType length, SizeType* outIndex) const { - for (SizeType index = 0; index < propertyCount_; index++) - if (properties_[index].name.GetStringLength() == length && std::memcmp(properties_[index].name.GetString(), str, length) == 0) { - *outIndex = index; - return true; - } - return false; - } - bool CheckInt(Context& context, int64_t i) const { if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) - RAPIDJSON_INVALID_KEYWORD_RETURN("type"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); if (!minimum_.IsNull()) { if (minimum_.IsInt64()) { if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) - RAPIDJSON_INVALID_KEYWORD_RETURN("minimum"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); } else if (!CheckDoubleMinimum(context, static_cast(i))) return false; @@ -983,7 +1051,7 @@ private: if (!maximum_.IsNull()) { if (maximum_.IsInt64()) { if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) - RAPIDJSON_INVALID_KEYWORD_RETURN("maximum"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); } else if (!CheckDoubleMaximum(context, static_cast(i))) return false; @@ -992,7 +1060,7 @@ private: if (!multipleOf_.IsNull()) { if (multipleOf_.IsUint64()) { if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) - RAPIDJSON_INVALID_KEYWORD_RETURN("multipleOf"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); } else if (!CheckDoubleMultipleOf(context, static_cast(i))) return false; @@ -1003,12 +1071,12 @@ private: bool CheckUint(Context& context, uint64_t i) const { if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) - RAPIDJSON_INVALID_KEYWORD_RETURN("type"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); if (!minimum_.IsNull()) { if (minimum_.IsUint64()) { if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) - RAPIDJSON_INVALID_KEYWORD_RETURN("minimum"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); } else if (!CheckDoubleMinimum(context, static_cast(i))) return false; @@ -1017,7 +1085,7 @@ private: if (!maximum_.IsNull()) { if (maximum_.IsUint64()) { if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) - RAPIDJSON_INVALID_KEYWORD_RETURN("maximum"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); } else if (!CheckDoubleMaximum(context, static_cast(i))) return false; @@ -1026,7 +1094,7 @@ private: if (!multipleOf_.IsNull()) { if (multipleOf_.IsUint64()) { if (i % multipleOf_.GetUint64() != 0) - RAPIDJSON_INVALID_KEYWORD_RETURN("multipleOf"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); } else if (!CheckDoubleMultipleOf(context, static_cast(i))) return false; @@ -1037,13 +1105,13 @@ private: bool CheckDoubleMinimum(Context& context, double d) const { if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) - RAPIDJSON_INVALID_KEYWORD_RETURN("minimum"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); return true; } bool CheckDoubleMaximum(Context& context, double d) const { if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) - RAPIDJSON_INVALID_KEYWORD_RETURN("maximum"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); return true; } @@ -1052,7 +1120,7 @@ private: double q = std::floor(a / b); double r = a - q * b; if (r > 0.0) - RAPIDJSON_INVALID_KEYWORD_RETURN("multipleOf"); + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); return true; } @@ -1245,8 +1313,10 @@ private: } } - bool HandleRefSchema(const Pointer& source, const SchemaType** schema, const ValueType& v) { - typename ValueType::ConstMemberIterator itr = v.FindMember("$ref"); + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v) { + static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' }; + + typename ValueType::ConstMemberIterator itr = v.FindMember(kRefString); if (itr == v.MemberEnd()) return false; @@ -1386,7 +1456,7 @@ public: return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema()); } - const char* GetInvalidSchemaKeyword() const { + const Ch* GetInvalidSchemaKeyword() const { return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword; } @@ -1399,7 +1469,7 @@ public: RAPIDJSON_MULTILINEMACRO_BEGIN\ *documentStack_.template Push() = '\0';\ documentStack_.template Pop(1);\ - printf("Fail document: %s\n\n", documentStack_.template Bottom());\ + internal::PrintInvalidDocument(documentStack_.template Bottom());\ RAPIDJSON_MULTILINEMACRO_END #else #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() @@ -1563,12 +1633,12 @@ private: return false; #if RAPIDJSON_SCHEMA_VERBOSE - StringBuffer sb; + GenericStringBuffer sb; schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb); *documentStack_.template Push() = '\0'; documentStack_.template Pop(1); - printf("S: %*s%s\nD: %*s%s\n\n", depth_ * 4, " ", sb.GetString(), depth_ * 4, " ", documentStack_.template Bottom()); + internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom()); #endif uint64_t h = CurrentContext().arrayUniqueness ? CurrentContext().hasher->GetHashCode() : 0; @@ -1580,7 +1650,7 @@ private: if (context.valueUniqueness) { for (typename Context::HashCodeArray::ConstValueIterator itr = context.arrayElementHashCodes.Begin(); itr != context.arrayElementHashCodes.End(); ++itr) if (itr->GetUint64() == h) - RAPIDJSON_INVALID_KEYWORD_RETURN("uniqueItems"); + RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString()); context.arrayElementHashCodes.PushBack(h, *context.allocator); } } @@ -1624,7 +1694,7 @@ private: static const size_t kDefaultSchemaStackCapacity = 1024; static const size_t kDefaultDocumentStackCapacity = 256; - const SchemaDocument* schemaDocument_; + const SchemaDocumentType* schemaDocument_; const SchemaType& root_; BaseReaderHandler nullOutputHandler_; OutputHandler& outputHandler_; diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index 41b0559..5cdb6ca 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -386,8 +386,8 @@ TEST(SchemaValidator, Integer_MultipleOf) { sd.Parse("{\"type\":\"integer\",\"multipleOf\":10}"); SchemaDocument s(sd); - // VALIDATE(s, "0", true); - // VALIDATE(s, "10", true); + VALIDATE(s, "0", true); + VALIDATE(s, "10", true); VALIDATE(s, "-10", true); VALIDATE(s, "20", true); INVALIDATE(s, "23", "", "multipleOf", ""); @@ -882,7 +882,33 @@ TEST(SchemaValidator, ValidateMetaSchema) { sb.Clear(); validator.GetInvalidDocumentPointer().StringifyUriFragment(sb); printf("Invalid document: %s\n", sb.GetString()); - //ADD_FAILURE(); + ADD_FAILURE(); + } + free(json); +} + +TEST(SchemaValidator, ValidateMetaSchema_UTF16) { + typedef GenericDocument > D; + typedef GenericSchemaDocument SD; + typedef GenericSchemaValidator SV; + + char* json = ReadFile("draft-04/schema"); + + D d; + StringStream ss(json); + d.ParseStream<0, UTF8<> >(ss); + ASSERT_FALSE(d.HasParseError()); + SD sd(d); + SV validator(sd); + if (!d.Accept(validator)) { + GenericStringBuffer > sb; + validator.GetInvalidSchemaPointer().StringifyUriFragment(sb); + wprintf(L"Invalid schema: %ls\n", sb.GetString()); + wprintf(L"Invalid keyword: %ls\n", validator.GetInvalidSchemaKeyword()); + sb.Clear(); + validator.GetInvalidDocumentPointer().StringifyUriFragment(sb); + wprintf(L"Invalid document: %ls\n", sb.GetString()); + ADD_FAILURE(); } free(json); }