Chromium Code Reviews| Index: components/json_schema/json_schema_validator.cc |
| diff --git a/components/json_schema/json_schema_validator.cc b/components/json_schema/json_schema_validator.cc |
| index 18558d0e111213bd789ac96633b8bc43afaaa53f..cf45e91a150d401a2182eb96f29d5eadb3b00cea 100644 |
| --- a/components/json_schema/json_schema_validator.cc |
| +++ b/components/json_schema/json_schema_validator.cc |
| @@ -7,13 +7,17 @@ |
| #include <algorithm> |
| #include <cfloat> |
| #include <cmath> |
| +#include <vector> |
| #include "base/json/json_reader.h" |
| +#include "base/logging.h" |
| +#include "base/memory/scoped_vector.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/values.h" |
| #include "components/json_schema/json_schema_constants.h" |
| +#include "third_party/re2/re2/re2.h" |
| namespace schema = json_schema_constants; |
| @@ -86,6 +90,8 @@ bool IsValidSchema(const base::DictionaryValue* dict, |
| { schema::kMinLength, base::Value::TYPE_INTEGER }, |
| { schema::kMinimum, base::Value::TYPE_DOUBLE }, |
| { schema::kOptional, base::Value::TYPE_BOOLEAN }, |
| + { schema::kPattern, base::Value::TYPE_STRING }, |
| + { schema::kPatternProperties, base::Value::TYPE_DICTIONARY }, |
| { schema::kProperties, base::Value::TYPE_DICTIONARY }, |
| { schema::kTitle, base::Value::TYPE_STRING }, |
| }; |
| @@ -186,10 +192,27 @@ bool IsValidSchema(const base::DictionaryValue* dict, |
| // Validate the "properties" attribute. Each entry maps a key to a schema. |
| if (it.key() == schema::kProperties) { |
| it.value().GetAsDictionary(&dictionary_value); |
| - for (base::DictionaryValue::Iterator it(*dictionary_value); |
| - !it.IsAtEnd(); it.Advance()) { |
| - if (!it.value().GetAsDictionary(&dictionary_value)) { |
| - *error = "Invalid value for properties attribute"; |
| + for (base::DictionaryValue::Iterator iter(*dictionary_value); |
| + !iter.IsAtEnd(); iter.Advance()) { |
| + if (!iter.value().GetAsDictionary(&dictionary_value)) { |
| + *error = "properties must be a dictionary"; |
| + return false; |
| + } |
| + if (!IsValidSchema(dictionary_value, options, error)) { |
| + DCHECK(!error->empty()); |
| + return false; |
| + } |
| + } |
| + } |
| + |
| + // Validate the "patternProperties" attribute. Each entry maps a valid |
| + // regular expression to a schema. |
| + if (it.key() == schema::kPatternProperties) { |
| + it.value().GetAsDictionary(&dictionary_value); |
| + for (base::DictionaryValue::Iterator iter(*dictionary_value); |
| + !iter.IsAtEnd(); iter.Advance()) { |
| + if (!iter.value().GetAsDictionary(&dictionary_value)) { |
| + *error = "patternProperty must be a dictionary"; |
|
Joao da Silva
2014/03/21 10:15:16
patternProperties
binjin
2014/03/21 14:57:43
Done.
|
| return false; |
| } |
| if (!IsValidSchema(dictionary_value, options, error)) { |
| @@ -308,6 +331,8 @@ const char JSONSchemaValidator::kInvalidType[] = |
| "Expected '*' but got '*'."; |
| const char JSONSchemaValidator::kInvalidTypeIntegerNumber[] = |
| "Expected 'integer' but got 'number', consider using Math.round()."; |
| +const char JSONSchemaValidator::kInvalidRegex[] = |
| + "Regular expression /*/ in Schema is invalid."; |
|
Joao da Silva
2014/03/21 10:15:16
Change this to: "Regular expression /*/ is invalid
binjin
2014/03/21 14:57:43
Done. As a note I don't plan to add tests for this
|
| // static |
| @@ -551,8 +576,7 @@ void JSONSchemaValidator::ValidateObject(const base::DictionaryValue* instance, |
| const base::DictionaryValue* schema, |
| const std::string& path) { |
| const base::DictionaryValue* properties = NULL; |
| - schema->GetDictionary(schema::kProperties, &properties); |
| - if (properties) { |
| + if (schema->GetDictionary(schema::kProperties, &properties)) { |
| for (base::DictionaryValue::Iterator it(*properties); !it.IsAtEnd(); |
| it.Advance()) { |
| std::string prop_path = path.empty() ? it.key() : (path + "." + it.key()); |
| @@ -574,17 +598,52 @@ void JSONSchemaValidator::ValidateObject(const base::DictionaryValue* instance, |
| } |
| } |
| + // Allowing any additional items will ignore pattern properties as well. |
| const base::DictionaryValue* additional_properties_schema = NULL; |
| - if (SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema)) |
| - return; |
| + bool allow_any_additional_properties = |
| + SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema); |
| - // Validate additional properties. |
| + const base::DictionaryValue* pattern_properties = NULL; |
| + ScopedVector<re2::RE2> pattern_properties_pattern; |
| + std::vector<const base::DictionaryValue*> pattern_properties_schema; |
| + |
| + if (schema->GetDictionary(schema::kPatternProperties, &pattern_properties)) { |
| + for (base::DictionaryValue::Iterator it(*pattern_properties); !it.IsAtEnd(); |
| + it.Advance()) { |
| + re2::RE2* prop_pattern = new re2::RE2(it.key()); |
| + if (!prop_pattern->ok()) { |
| + LOG(WARNING) << "Regular expression /" << it.key() |
| + << "/ is invalid: " << prop_pattern->error() << "."; |
| + errors_.push_back( |
| + Error(path, FormatErrorMessage(kInvalidRegex, it.key()))); |
|
Joao da Silva
2014/03/21 10:15:16
Include prop_pattern->error() in this Error (see k
binjin
2014/03/21 14:57:43
Done.
|
| + } |
| + const base::DictionaryValue* prop_schema = NULL; |
| + CHECK(it.value().GetAsDictionary(&prop_schema)); |
| + pattern_properties_pattern.push_back(prop_pattern); |
| + pattern_properties_schema.push_back(prop_schema); |
| + } |
| + } |
| + |
| + // Validate pattern properties and additional properties. |
| for (base::DictionaryValue::Iterator it(*instance); !it.IsAtEnd(); |
| it.Advance()) { |
| - if (properties && properties->HasKey(it.key())) |
| + std::string prop_path = path.empty() ? it.key() : path + "." + it.key(); |
| + |
| + bool found_matching_pattern = false; |
| + for (size_t index = 0; index < pattern_properties_pattern.size(); ++index) { |
| + if (pattern_properties_pattern[index]->ok() && |
| + re2::RE2::PartialMatch(it.key(), |
| + *pattern_properties_pattern[index])) { |
| + found_matching_pattern = true; |
| + Validate(&it.value(), pattern_properties_schema[index], prop_path); |
| + break; |
| + } |
| + } |
| + |
| + if (found_matching_pattern || allow_any_additional_properties || |
| + (properties && properties->HasKey(it.key()))) |
| continue; |
| - std::string prop_path = path.empty() ? it.key() : path + "." + it.key(); |
| if (!additional_properties_schema) { |
| errors_.push_back(Error(prop_path, kUnexpectedProperty)); |
| } else { |
| @@ -707,7 +766,19 @@ void JSONSchemaValidator::ValidateString(const base::Value* instance, |
| } |
| } |
| - CHECK(!schema->HasKey(schema::kPattern)) << "Pattern is not supported."; |
| + std::string pattern; |
| + if (schema->GetString(schema::kPattern, &pattern)) { |
| + re2::RE2 compiled_regex(pattern); |
| + if (!compiled_regex.ok()) { |
| + LOG(WARNING) << "Regular expression /" << pattern |
| + << "/ is invalid: " << compiled_regex.error() << "."; |
| + errors_.push_back( |
| + Error(path, FormatErrorMessage(kInvalidRegex, pattern))); |
|
Joao da Silva
2014/03/21 10:15:16
Include compiled_reger.error() in this Error too (
binjin
2014/03/21 14:57:43
Done.
|
| + } else if (!re2::RE2::PartialMatch(value, compiled_regex)) { |
| + errors_.push_back( |
| + Error(path, FormatErrorMessage(kStringPattern, pattern))); |
|
Joao da Silva
2014/03/21 10:15:16
Include compiled_reger.error() in this Error too (
binjin
2014/03/21 14:57:43
Not changed, as mentioned above.
|
| + } |
| + } |
| } |
| void JSONSchemaValidator::ValidateNumber(const base::Value* instance, |