diff options
author | binjin@chromium.org <binjin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-27 18:58:37 +0000 |
---|---|---|
committer | binjin@chromium.org <binjin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-27 18:58:37 +0000 |
commit | 183ce73b72541370cbcd26dc9be9a6019f1f533c (patch) | |
tree | 9433abece2473dbe99440a9f87c4124f1f6c9a9d /components/json_schema/json_schema_validator.cc | |
parent | 34ce6290c7246b814a9b032f6c8a526e5f79ffb7 (diff) | |
download | chromium_src-183ce73b72541370cbcd26dc9be9a6019f1f533c.zip chromium_src-183ce73b72541370cbcd26dc9be9a6019f1f533c.tar.gz chromium_src-183ce73b72541370cbcd26dc9be9a6019f1f533c.tar.bz2 |
Add regular expression support in json_schema component
Add support of "pattern" and "patternProperties" attribute into json_schema, with third_party/re2 as regular expression implementation.
BUG=348551
Review URL: https://codereview.chromium.org/195193002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@259943 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components/json_schema/json_schema_validator.cc')
-rw-r--r-- | components/json_schema/json_schema_validator.cc | 99 |
1 files changed, 87 insertions, 12 deletions
diff --git a/components/json_schema/json_schema_validator.cc b/components/json_schema/json_schema_validator.cc index 18558d0..1899ad6 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,29 @@ 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 regular + // expression to a schema. The validity of the regular expression expression + // won't be checked here for performance reasons. Instead, invalid regular + // expressions will be caught as validation errors in Validate(). + 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 = "patternProperties must be a dictionary"; return false; } if (!IsValidSchema(dictionary_value, options, error)) { @@ -308,6 +333,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 /*/ is invalid: *"; // static @@ -551,8 +578,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()); @@ -575,16 +601,52 @@ void JSONSchemaValidator::ValidateObject(const base::DictionaryValue* instance, } 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(), prop_pattern->error()))); + continue; + } + 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 (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 +769,20 @@ 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, compiled_regex.error()))); + } else if (!re2::RE2::PartialMatch(value, compiled_regex)) { + errors_.push_back( + Error(path, FormatErrorMessage(kStringPattern, pattern))); + } + } } void JSONSchemaValidator::ValidateNumber(const base::Value* instance, |