OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/json_schema/json_schema_validator.h" | 5 #include "components/json_schema/json_schema_validator.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <cfloat> | 10 #include <cfloat> |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
72 return value; | 72 return value; |
73 } | 73 } |
74 | 74 |
75 bool IsValidSchema(const base::DictionaryValue* dict, | 75 bool IsValidSchema(const base::DictionaryValue* dict, |
76 int options, | 76 int options, |
77 std::string* error) { | 77 std::string* error) { |
78 // This array must be sorted, so that std::lower_bound can perform a | 78 // This array must be sorted, so that std::lower_bound can perform a |
79 // binary search. | 79 // binary search. |
80 static const ExpectedType kExpectedTypes[] = { | 80 static const ExpectedType kExpectedTypes[] = { |
81 // Note: kRef == "$ref", kSchema == "$schema" | 81 // Note: kRef == "$ref", kSchema == "$schema" |
82 { schema::kRef, base::Value::TYPE_STRING }, | 82 { schema::kRef, base::Value::Type::STRING }, |
83 { schema::kSchema, base::Value::TYPE_STRING }, | 83 { schema::kSchema, base::Value::Type::STRING }, |
84 | 84 |
85 { schema::kAdditionalProperties, base::Value::TYPE_DICTIONARY }, | 85 { schema::kAdditionalProperties, base::Value::Type::DICTIONARY }, |
86 { schema::kChoices, base::Value::TYPE_LIST }, | 86 { schema::kChoices, base::Value::Type::LIST }, |
87 { schema::kDescription, base::Value::TYPE_STRING }, | 87 { schema::kDescription, base::Value::Type::STRING }, |
88 { schema::kEnum, base::Value::TYPE_LIST }, | 88 { schema::kEnum, base::Value::Type::LIST }, |
89 { schema::kId, base::Value::TYPE_STRING }, | 89 { schema::kId, base::Value::Type::STRING }, |
90 { schema::kMaxItems, base::Value::TYPE_INTEGER }, | 90 { schema::kMaxItems, base::Value::Type::INTEGER }, |
91 { schema::kMaxLength, base::Value::TYPE_INTEGER }, | 91 { schema::kMaxLength, base::Value::Type::INTEGER }, |
92 { schema::kMaximum, base::Value::TYPE_DOUBLE }, | 92 { schema::kMaximum, base::Value::Type::DOUBLE }, |
93 { schema::kMinItems, base::Value::TYPE_INTEGER }, | 93 { schema::kMinItems, base::Value::Type::INTEGER }, |
94 { schema::kMinLength, base::Value::TYPE_INTEGER }, | 94 { schema::kMinLength, base::Value::Type::INTEGER }, |
95 { schema::kMinimum, base::Value::TYPE_DOUBLE }, | 95 { schema::kMinimum, base::Value::Type::DOUBLE }, |
96 { schema::kOptional, base::Value::TYPE_BOOLEAN }, | 96 { schema::kOptional, base::Value::Type::BOOLEAN }, |
97 { schema::kPattern, base::Value::TYPE_STRING }, | 97 { schema::kPattern, base::Value::Type::STRING }, |
98 { schema::kPatternProperties, base::Value::TYPE_DICTIONARY }, | 98 { schema::kPatternProperties, base::Value::Type::DICTIONARY }, |
99 { schema::kProperties, base::Value::TYPE_DICTIONARY }, | 99 { schema::kProperties, base::Value::Type::DICTIONARY }, |
100 { schema::kTitle, base::Value::TYPE_STRING }, | 100 { schema::kTitle, base::Value::Type::STRING }, |
101 }; | 101 }; |
102 | 102 |
103 bool has_type_or_ref = false; | 103 bool has_type_or_ref = false; |
104 const base::ListValue* list_value = NULL; | 104 const base::ListValue* list_value = NULL; |
105 const base::DictionaryValue* dictionary_value = NULL; | 105 const base::DictionaryValue* dictionary_value = NULL; |
106 std::string string_value; | 106 std::string string_value; |
107 | 107 |
108 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) { | 108 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) { |
109 // Validate the "type" attribute, which may be a string or a list. | 109 // Validate the "type" attribute, which may be a string or a list. |
110 if (it.key() == schema::kType) { | 110 if (it.key() == schema::kType) { |
111 switch (it.value().GetType()) { | 111 switch (it.value().GetType()) { |
112 case base::Value::TYPE_STRING: | 112 case base::Value::Type::STRING: |
113 it.value().GetAsString(&string_value); | 113 it.value().GetAsString(&string_value); |
114 if (!IsValidType(string_value)) { | 114 if (!IsValidType(string_value)) { |
115 *error = "Invalid value for type attribute"; | 115 *error = "Invalid value for type attribute"; |
116 return false; | 116 return false; |
117 } | 117 } |
118 break; | 118 break; |
119 case base::Value::TYPE_LIST: | 119 case base::Value::Type::LIST: |
120 it.value().GetAsList(&list_value); | 120 it.value().GetAsList(&list_value); |
121 for (size_t i = 0; i < list_value->GetSize(); ++i) { | 121 for (size_t i = 0; i < list_value->GetSize(); ++i) { |
122 if (!list_value->GetString(i, &string_value) || | 122 if (!list_value->GetString(i, &string_value) || |
123 !IsValidType(string_value)) { | 123 !IsValidType(string_value)) { |
124 *error = "Invalid value for type attribute"; | 124 *error = "Invalid value for type attribute"; |
125 return false; | 125 return false; |
126 } | 126 } |
127 } | 127 } |
128 break; | 128 break; |
129 default: | 129 default: |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
167 kExpectedTypes, end, it.key(), CompareToString); | 167 kExpectedTypes, end, it.key(), CompareToString); |
168 if (entry == end || entry->key != it.key()) { | 168 if (entry == end || entry->key != it.key()) { |
169 if (options & JSONSchemaValidator::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES) | 169 if (options & JSONSchemaValidator::OPTIONS_IGNORE_UNKNOWN_ATTRIBUTES) |
170 continue; | 170 continue; |
171 *error = base::StringPrintf("Invalid attribute %s", it.key().c_str()); | 171 *error = base::StringPrintf("Invalid attribute %s", it.key().c_str()); |
172 return false; | 172 return false; |
173 } | 173 } |
174 | 174 |
175 // Integer can be converted to double. | 175 // Integer can be converted to double. |
176 if (!(it.value().IsType(entry->type) || | 176 if (!(it.value().IsType(entry->type) || |
177 (it.value().IsType(base::Value::TYPE_INTEGER) && | 177 (it.value().IsType(base::Value::Type::INTEGER) && |
178 entry->type == base::Value::TYPE_DOUBLE))) { | 178 entry->type == base::Value::Type::DOUBLE))) { |
179 *error = base::StringPrintf("Invalid value for %s attribute", | 179 *error = base::StringPrintf("Invalid value for %s attribute", |
180 it.key().c_str()); | 180 it.key().c_str()); |
181 return false; | 181 return false; |
182 } | 182 } |
183 | 183 |
184 // base::Value::TYPE_INTEGER attributes must be >= 0. | 184 // base::Value::Type::INTEGER attributes must be >= 0. |
185 // This applies to "minItems", "maxItems", "minLength" and "maxLength". | 185 // This applies to "minItems", "maxItems", "minLength" and "maxLength". |
186 if (it.value().IsType(base::Value::TYPE_INTEGER)) { | 186 if (it.value().IsType(base::Value::Type::INTEGER)) { |
187 int integer_value; | 187 int integer_value; |
188 it.value().GetAsInteger(&integer_value); | 188 it.value().GetAsInteger(&integer_value); |
189 if (integer_value < 0) { | 189 if (integer_value < 0) { |
190 *error = base::StringPrintf("Value of %s must be >= 0, got %d", | 190 *error = base::StringPrintf("Value of %s must be >= 0, got %d", |
191 it.key().c_str(), integer_value); | 191 it.key().c_str(), integer_value); |
192 return false; | 192 return false; |
193 } | 193 } |
194 } | 194 } |
195 | 195 |
196 // Validate the "properties" attribute. Each entry maps a key to a schema. | 196 // Validate the "properties" attribute. Each entry maps a key to a schema. |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
244 const base::Value* value = NULL; | 244 const base::Value* value = NULL; |
245 list_value->Get(i, &value); | 245 list_value->Get(i, &value); |
246 // Sometimes the enum declaration is a dictionary with the enum value | 246 // Sometimes the enum declaration is a dictionary with the enum value |
247 // under "name". | 247 // under "name". |
248 value = ExtractNameFromDictionary(value); | 248 value = ExtractNameFromDictionary(value); |
249 if (!value) { | 249 if (!value) { |
250 *error = "Invalid value in enum attribute"; | 250 *error = "Invalid value in enum attribute"; |
251 return false; | 251 return false; |
252 } | 252 } |
253 switch (value->GetType()) { | 253 switch (value->GetType()) { |
254 case base::Value::TYPE_NULL: | 254 case base::Value::Type::NONE: |
255 case base::Value::TYPE_BOOLEAN: | 255 case base::Value::Type::BOOLEAN: |
256 case base::Value::TYPE_INTEGER: | 256 case base::Value::Type::INTEGER: |
257 case base::Value::TYPE_DOUBLE: | 257 case base::Value::Type::DOUBLE: |
258 case base::Value::TYPE_STRING: | 258 case base::Value::Type::STRING: |
259 break; | 259 break; |
260 default: | 260 default: |
261 *error = "Invalid value in enum attribute"; | 261 *error = "Invalid value in enum attribute"; |
262 return false; | 262 return false; |
263 } | 263 } |
264 } | 264 } |
265 } | 265 } |
266 | 266 |
267 // Validate the schemas contained in a "choices" attribute. | 267 // Validate the schemas contained in a "choices" attribute. |
268 if (it.key() == schema::kChoices) { | 268 if (it.key() == schema::kChoices) { |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
337 "Expected '*' but got '*'."; | 337 "Expected '*' but got '*'."; |
338 const char JSONSchemaValidator::kInvalidTypeIntegerNumber[] = | 338 const char JSONSchemaValidator::kInvalidTypeIntegerNumber[] = |
339 "Expected 'integer' but got 'number', consider using Math.round()."; | 339 "Expected 'integer' but got 'number', consider using Math.round()."; |
340 const char JSONSchemaValidator::kInvalidRegex[] = | 340 const char JSONSchemaValidator::kInvalidRegex[] = |
341 "Regular expression /*/ is invalid: *"; | 341 "Regular expression /*/ is invalid: *"; |
342 | 342 |
343 | 343 |
344 // static | 344 // static |
345 std::string JSONSchemaValidator::GetJSONSchemaType(const base::Value* value) { | 345 std::string JSONSchemaValidator::GetJSONSchemaType(const base::Value* value) { |
346 switch (value->GetType()) { | 346 switch (value->GetType()) { |
347 case base::Value::TYPE_NULL: | 347 case base::Value::Type::NONE: |
348 return schema::kNull; | 348 return schema::kNull; |
349 case base::Value::TYPE_BOOLEAN: | 349 case base::Value::Type::BOOLEAN: |
350 return schema::kBoolean; | 350 return schema::kBoolean; |
351 case base::Value::TYPE_INTEGER: | 351 case base::Value::Type::INTEGER: |
352 return schema::kInteger; | 352 return schema::kInteger; |
353 case base::Value::TYPE_DOUBLE: { | 353 case base::Value::Type::DOUBLE: { |
354 double double_value = 0; | 354 double double_value = 0; |
355 value->GetAsDouble(&double_value); | 355 value->GetAsDouble(&double_value); |
356 if (std::abs(double_value) <= std::pow(2.0, DBL_MANT_DIG) && | 356 if (std::abs(double_value) <= std::pow(2.0, DBL_MANT_DIG) && |
357 double_value == floor(double_value)) { | 357 double_value == floor(double_value)) { |
358 return schema::kInteger; | 358 return schema::kInteger; |
359 } else { | 359 } else { |
360 return schema::kNumber; | 360 return schema::kNumber; |
361 } | 361 } |
362 } | 362 } |
363 case base::Value::TYPE_STRING: | 363 case base::Value::Type::STRING: |
364 return schema::kString; | 364 return schema::kString; |
365 case base::Value::TYPE_DICTIONARY: | 365 case base::Value::Type::DICTIONARY: |
366 return schema::kObject; | 366 return schema::kObject; |
367 case base::Value::TYPE_LIST: | 367 case base::Value::Type::LIST: |
368 return schema::kArray; | 368 return schema::kArray; |
369 default: | 369 default: |
370 NOTREACHED() << "Unexpected value type: " << value->GetType(); | 370 NOTREACHED() << "Unexpected value type: " << value->GetType(); |
371 return std::string(); | 371 return std::string(); |
372 } | 372 } |
373 } | 373 } |
374 | 374 |
375 // static | 375 // static |
376 std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format, | 376 std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format, |
377 const std::string& s1) { | 377 const std::string& s1) { |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
500 | 500 |
501 // These casts are safe because of checks in ValidateType(). | 501 // These casts are safe because of checks in ValidateType(). |
502 if (type == schema::kObject) { | 502 if (type == schema::kObject) { |
503 ValidateObject(static_cast<const base::DictionaryValue*>(instance), | 503 ValidateObject(static_cast<const base::DictionaryValue*>(instance), |
504 schema, | 504 schema, |
505 path); | 505 path); |
506 } else if (type == schema::kArray) { | 506 } else if (type == schema::kArray) { |
507 ValidateArray(static_cast<const base::ListValue*>(instance), | 507 ValidateArray(static_cast<const base::ListValue*>(instance), |
508 schema, path); | 508 schema, path); |
509 } else if (type == schema::kString) { | 509 } else if (type == schema::kString) { |
510 // Intentionally NOT downcasting to StringValue*. TYPE_STRING only implies | 510 // Intentionally NOT downcasting to StringValue*. Type::STRING only |
511 // GetAsString() can safely be carried out, not that it's a StringValue. | 511 // implies GetAsString() can safely be carried out, not that it's a |
| 512 // StringValue. |
512 ValidateString(instance, schema, path); | 513 ValidateString(instance, schema, path); |
513 } else if (type == schema::kNumber || type == schema::kInteger) { | 514 } else if (type == schema::kNumber || type == schema::kInteger) { |
514 ValidateNumber(instance, schema, path); | 515 ValidateNumber(instance, schema, path); |
515 } else if (type != schema::kBoolean && type != schema::kNull) { | 516 } else if (type != schema::kBoolean && type != schema::kNull) { |
516 NOTREACHED() << "Unexpected type: " << type; | 517 NOTREACHED() << "Unexpected type: " << type; |
517 } | 518 } |
518 } | 519 } |
519 } | 520 } |
520 | 521 |
521 void JSONSchemaValidator::ValidateChoices(const base::Value* instance, | 522 void JSONSchemaValidator::ValidateChoices(const base::Value* instance, |
(...skipping 25 matching lines...) Expand all Loading... |
547 for (size_t i = 0; i < choices->GetSize(); ++i) { | 548 for (size_t i = 0; i < choices->GetSize(); ++i) { |
548 const base::Value* choice = NULL; | 549 const base::Value* choice = NULL; |
549 CHECK(choices->Get(i, &choice)); | 550 CHECK(choices->Get(i, &choice)); |
550 // Sometimes the enum declaration is a dictionary with the enum value under | 551 // Sometimes the enum declaration is a dictionary with the enum value under |
551 // "name". | 552 // "name". |
552 choice = ExtractNameFromDictionary(choice); | 553 choice = ExtractNameFromDictionary(choice); |
553 if (!choice) { | 554 if (!choice) { |
554 NOTREACHED(); | 555 NOTREACHED(); |
555 } | 556 } |
556 switch (choice->GetType()) { | 557 switch (choice->GetType()) { |
557 case base::Value::TYPE_NULL: | 558 case base::Value::Type::NONE: |
558 case base::Value::TYPE_BOOLEAN: | 559 case base::Value::Type::BOOLEAN: |
559 case base::Value::TYPE_STRING: | 560 case base::Value::Type::STRING: |
560 if (instance->Equals(choice)) | 561 if (instance->Equals(choice)) |
561 return; | 562 return; |
562 break; | 563 break; |
563 | 564 |
564 case base::Value::TYPE_INTEGER: | 565 case base::Value::Type::INTEGER: |
565 case base::Value::TYPE_DOUBLE: | 566 case base::Value::Type::DOUBLE: |
566 if (instance->IsType(base::Value::TYPE_INTEGER) || | 567 if (instance->IsType(base::Value::Type::INTEGER) || |
567 instance->IsType(base::Value::TYPE_DOUBLE)) { | 568 instance->IsType(base::Value::Type::DOUBLE)) { |
568 if (GetNumberValue(choice) == GetNumberValue(instance)) | 569 if (GetNumberValue(choice) == GetNumberValue(instance)) |
569 return; | 570 return; |
570 } | 571 } |
571 break; | 572 break; |
572 | 573 |
573 default: | 574 default: |
574 NOTREACHED() << "Unexpected type in enum: " << choice->GetType(); | 575 NOTREACHED() << "Unexpected type in enum: " << choice->GetType(); |
575 } | 576 } |
576 } | 577 } |
577 | 578 |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
708 schema->GetList(schema::kItems, &tuple_type); | 709 schema->GetList(schema::kItems, &tuple_type); |
709 size_t tuple_size = tuple_type ? tuple_type->GetSize() : 0; | 710 size_t tuple_size = tuple_type ? tuple_type->GetSize() : 0; |
710 if (tuple_type) { | 711 if (tuple_type) { |
711 for (size_t i = 0; i < tuple_size; ++i) { | 712 for (size_t i = 0; i < tuple_size; ++i) { |
712 std::string i_str = base::Uint64ToString(i); | 713 std::string i_str = base::Uint64ToString(i); |
713 std::string item_path = path.empty() ? i_str : (path + "." + i_str); | 714 std::string item_path = path.empty() ? i_str : (path + "." + i_str); |
714 const base::DictionaryValue* item_schema = NULL; | 715 const base::DictionaryValue* item_schema = NULL; |
715 CHECK(tuple_type->GetDictionary(i, &item_schema)); | 716 CHECK(tuple_type->GetDictionary(i, &item_schema)); |
716 const base::Value* item_value = NULL; | 717 const base::Value* item_value = NULL; |
717 instance->Get(i, &item_value); | 718 instance->Get(i, &item_value); |
718 if (item_value && item_value->GetType() != base::Value::TYPE_NULL) { | 719 if (item_value && item_value->GetType() != base::Value::Type::NONE) { |
719 Validate(item_value, item_schema, item_path); | 720 Validate(item_value, item_schema, item_path); |
720 } else { | 721 } else { |
721 bool is_optional = false; | 722 bool is_optional = false; |
722 item_schema->GetBoolean(schema::kOptional, &is_optional); | 723 item_schema->GetBoolean(schema::kOptional, &is_optional); |
723 if (!is_optional) { | 724 if (!is_optional) { |
724 errors_.push_back(Error(item_path, kArrayItemRequired)); | 725 errors_.push_back(Error(item_path, kArrayItemRequired)); |
725 return; | 726 return; |
726 } | 727 } |
727 } | 728 } |
728 } | 729 } |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
840 | 841 |
841 if (*additional_properties_schema) { | 842 if (*additional_properties_schema) { |
842 std::string additional_properties_type(schema::kAny); | 843 std::string additional_properties_type(schema::kAny); |
843 CHECK((*additional_properties_schema)->GetString( | 844 CHECK((*additional_properties_schema)->GetString( |
844 schema::kType, &additional_properties_type)); | 845 schema::kType, &additional_properties_type)); |
845 return additional_properties_type == schema::kAny; | 846 return additional_properties_type == schema::kAny; |
846 } else { | 847 } else { |
847 return default_allow_additional_properties_; | 848 return default_allow_additional_properties_; |
848 } | 849 } |
849 } | 850 } |
OLD | NEW |