OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "chrome/common/json_schema/json_schema_validator.h" | 5 #include "chrome/common/json_schema/json_schema_validator.h" |
6 | 6 |
7 #include <algorithm> | |
7 #include <cfloat> | 8 #include <cfloat> |
8 #include <cmath> | 9 #include <cmath> |
9 | 10 |
11 #include "base/json/json_reader.h" | |
10 #include "base/string_util.h" | 12 #include "base/string_util.h" |
13 #include "base/stringprintf.h" | |
11 #include "base/strings/string_number_conversions.h" | 14 #include "base/strings/string_number_conversions.h" |
12 #include "base/values.h" | 15 #include "base/values.h" |
13 #include "chrome/common/json_schema/json_schema_constants.h" | 16 #include "chrome/common/json_schema/json_schema_constants.h" |
14 #include "ui/base/l10n/l10n_util.h" | 17 #include "ui/base/l10n/l10n_util.h" |
15 | 18 |
16 namespace schema = json_schema_constants; | 19 namespace schema = json_schema_constants; |
17 | 20 |
18 namespace { | 21 namespace { |
19 | 22 |
20 double GetNumberValue(const Value* value) { | 23 double GetNumberValue(const Value* value) { |
21 double result = 0; | 24 double result = 0; |
22 CHECK(value->GetAsDouble(&result)) | 25 CHECK(value->GetAsDouble(&result)) |
23 << "Unexpected value type: " << value->GetType(); | 26 << "Unexpected value type: " << value->GetType(); |
24 return result; | 27 return result; |
25 } | 28 } |
26 | 29 |
30 bool IsValidType(const std::string& type) { | |
31 static const char* kValidTypes[] = { | |
32 schema::kAny, | |
33 schema::kArray, | |
34 schema::kBoolean, | |
35 schema::kInteger, | |
36 schema::kNull, | |
37 schema::kNumber, | |
38 schema::kObject, | |
39 schema::kString, | |
40 }; | |
41 const char** end = kValidTypes + arraysize(kValidTypes); | |
42 return std::find(kValidTypes, end, type) != end; | |
43 } | |
44 | |
45 // Maps a schema attribute name to its expected type. | |
46 struct ExpectedType { | |
47 const char* key; | |
48 base::Value::Type type; | |
49 }; | |
50 | |
51 // Helper for std::lower_bound. | |
52 bool CompareToString(const ExpectedType& entry, const std::string& key) { | |
53 return entry.key < key; | |
54 } | |
55 | |
56 bool IsValidSchema(const base::DictionaryValue* dict, std::string* error) { | |
57 // This array must be sorted, so that std::lower_bound can perform a | |
58 // binary search. | |
59 static const ExpectedType kExpectedTypes[] = { | |
60 // Note: kRef == "$ref", kSchema == "$schema" | |
61 { schema::kRef, base::Value::TYPE_STRING }, | |
62 { schema::kSchema, base::Value::TYPE_STRING }, | |
63 | |
64 { schema::kAdditionalProperties, base::Value::TYPE_DICTIONARY }, | |
65 { schema::kChoices, base::Value::TYPE_LIST }, | |
66 { schema::kDescription, base::Value::TYPE_STRING }, | |
67 { schema::kEnum, base::Value::TYPE_LIST }, | |
68 { schema::kId, base::Value::TYPE_STRING }, | |
69 { schema::kMaxItems, base::Value::TYPE_INTEGER }, | |
70 { schema::kMaxLength, base::Value::TYPE_INTEGER }, | |
71 { schema::kMaximum, base::Value::TYPE_DOUBLE }, | |
72 { schema::kMinItems, base::Value::TYPE_INTEGER }, | |
73 { schema::kMinLength, base::Value::TYPE_INTEGER }, | |
74 { schema::kMinimum, base::Value::TYPE_DOUBLE }, | |
75 { schema::kOptional, base::Value::TYPE_BOOLEAN }, | |
76 { schema::kProperties, base::Value::TYPE_DICTIONARY }, | |
77 { schema::kTitle, base::Value::TYPE_STRING }, | |
78 }; | |
79 | |
80 bool has_type = false; | |
81 const base::ListValue* list_value = NULL; | |
82 const base::DictionaryValue* dictionary_value = NULL; | |
83 std::string string_value; | |
84 | |
85 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) { | |
86 // Validate the "type" attribute, which may be a string or a list. | |
87 if (it.key() == schema::kType) { | |
88 switch (it.value().GetType()) { | |
89 case base::Value::TYPE_STRING: | |
90 it.value().GetAsString(&string_value); | |
91 if (!IsValidType(string_value)) { | |
92 *error = "Invalid value for type attribute"; | |
93 return false; | |
94 } | |
95 break; | |
96 case base::Value::TYPE_LIST: | |
97 it.value().GetAsList(&list_value); | |
98 for (size_t i = 0; i < list_value->GetSize(); ++i) { | |
99 if (!list_value->GetString(i, &string_value) || | |
100 !IsValidType(string_value)) { | |
101 *error = "Invalid value for type attribute"; | |
102 return false; | |
103 } | |
104 } | |
105 break; | |
106 default: | |
107 *error = "Invalid value for type attribute"; | |
108 return false; | |
109 } | |
110 has_type = true; | |
111 continue; | |
112 } | |
113 | |
114 // Validate the "items" attribute, which is a schema or a list of schemas. | |
115 if (it.key() == schema::kItems) { | |
116 if (it.value().GetAsDictionary(&dictionary_value)) { | |
117 if (!IsValidSchema(dictionary_value, error)) { | |
118 DCHECK(!error->empty()); | |
119 return false; | |
120 } | |
121 } else if (it.value().GetAsList(&list_value)) { | |
122 for (size_t i = 0; i < list_value->GetSize(); ++i) { | |
123 if (!list_value->GetDictionary(i, &dictionary_value)) { | |
124 *error = base::StringPrintf( | |
125 "Invalid entry in items attribute at index %d", | |
126 static_cast<int>(i)); | |
not at google - send to devlin
2013/05/20 21:08:52
can you use %u and avoid the static_cast? or does
Joao da Silva
2013/05/21 15:58:28
The problem is that size_t is 32 bits in some plat
| |
127 return false; | |
128 } | |
129 if (!IsValidSchema(dictionary_value, error)) { | |
130 DCHECK(!error->empty()); | |
131 return false; | |
132 } | |
133 } | |
134 } else { | |
135 *error = "Invalid value for items attribute"; | |
136 return false; | |
137 } | |
138 continue; | |
139 } | |
140 | |
141 // All the other attributes have a single valid type. | |
142 const ExpectedType* end = kExpectedTypes + arraysize(kExpectedTypes); | |
143 const ExpectedType* entry = std::lower_bound( | |
144 kExpectedTypes, end, it.key(), CompareToString); | |
145 if (entry == end || entry->key != it.key()) { | |
146 *error = base::StringPrintf("Invalid attribute %s", it.key().c_str()); | |
147 return false; | |
148 } | |
149 if (!it.value().IsType(entry->type)) { | |
150 *error = base::StringPrintf("Invalid value for %s attribute", | |
151 it.key().c_str()); | |
152 return false; | |
153 } | |
154 | |
155 // base::Value::TYPE_INTEGER attributes must be >= 0. | |
156 // This applies to "minItems", "maxItems", "minLength" and "maxLength". | |
157 if (it.value().IsType(base::Value::TYPE_INTEGER)) { | |
158 int integer_value; | |
159 it.value().GetAsInteger(&integer_value); | |
160 if (integer_value < 0) { | |
161 *error = base::StringPrintf("Value of %s must be >= 0, got %d", | |
162 it.key().c_str(), integer_value); | |
163 return false; | |
164 } | |
165 } | |
166 | |
167 // Validate the "properties" attribute. Each entry maps a key to a schema. | |
168 if (it.key() == schema::kProperties) { | |
169 it.value().GetAsDictionary(&dictionary_value); | |
170 for (base::DictionaryValue::Iterator it(*dictionary_value); | |
171 !it.IsAtEnd(); it.Advance()) { | |
172 if (!it.value().GetAsDictionary(&dictionary_value)) { | |
173 *error = "Invalid value for properties attribute"; | |
174 return false; | |
175 } | |
176 if (!IsValidSchema(dictionary_value, error)) { | |
177 DCHECK(!error->empty()); | |
178 return false; | |
179 } | |
180 } | |
181 } | |
182 | |
183 // Validate "additionalProperties" attribute, which is a schema. | |
184 if (it.key() == schema::kAdditionalProperties) { | |
185 it.value().GetAsDictionary(&dictionary_value); | |
186 if (!IsValidSchema(dictionary_value, error)) { | |
187 DCHECK(!error->empty()); | |
188 return false; | |
189 } | |
190 } | |
191 | |
192 // Validate the values contained in an "enum" attribute. | |
193 if (it.key() == schema::kEnum) { | |
194 it.value().GetAsList(&list_value); | |
195 for (size_t i = 0; i < list_value->GetSize(); ++i) { | |
196 const base::Value* value = NULL; | |
197 list_value->Get(i, &value); | |
198 switch (value->GetType()) { | |
199 case base::Value::TYPE_NULL: | |
200 case base::Value::TYPE_BOOLEAN: | |
201 case base::Value::TYPE_INTEGER: | |
202 case base::Value::TYPE_DOUBLE: | |
203 case base::Value::TYPE_STRING: | |
204 break; | |
205 default: | |
206 *error = "Invalid value in enum attribute"; | |
207 return false; | |
208 } | |
209 } | |
210 } | |
211 | |
212 // Validate the schemas contained in a "choices" attribute. | |
213 if (it.key() == schema::kChoices) { | |
214 it.value().GetAsList(&list_value); | |
215 for (size_t i = 0; i < list_value->GetSize(); ++i) { | |
216 if (!list_value->GetDictionary(i, &dictionary_value)) { | |
217 *error = "Invalid choices attribute"; | |
218 return false; | |
219 } | |
220 if (!IsValidSchema(dictionary_value, error)) { | |
221 DCHECK(!error->empty()); | |
222 return false; | |
223 } | |
224 } | |
225 } | |
226 } | |
227 | |
228 if (!has_type) { | |
229 *error = "Schema must have a type attribute"; | |
230 return false; | |
231 } | |
232 | |
233 return true; | |
234 } | |
235 | |
27 } // namespace | 236 } // namespace |
28 | 237 |
29 | 238 |
30 JSONSchemaValidator::Error::Error() { | 239 JSONSchemaValidator::Error::Error() { |
31 } | 240 } |
32 | 241 |
33 JSONSchemaValidator::Error::Error(const std::string& message) | 242 JSONSchemaValidator::Error::Error(const std::string& message) |
34 : path(message) { | 243 : path(message) { |
35 } | 244 } |
36 | 245 |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
114 // static | 323 // static |
115 std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format, | 324 std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format, |
116 const std::string& s1, | 325 const std::string& s1, |
117 const std::string& s2) { | 326 const std::string& s2) { |
118 std::string ret_val = format; | 327 std::string ret_val = format; |
119 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1); | 328 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1); |
120 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2); | 329 ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2); |
121 return ret_val; | 330 return ret_val; |
122 } | 331 } |
123 | 332 |
333 // static | |
334 scoped_ptr<base::DictionaryValue> JSONSchemaValidator::IsValidSchema( | |
335 const std::string& schema, | |
336 std::string* error) { | |
337 base::JSONParserOptions options = base::JSON_PARSE_RFC; | |
338 scoped_ptr<base::Value> json( | |
339 base::JSONReader::ReadAndReturnError(schema, options, NULL, error)); | |
340 if (!json) | |
341 return scoped_ptr<base::DictionaryValue>(); | |
342 base::DictionaryValue* dict = NULL; | |
343 if (!json->GetAsDictionary(&dict)) { | |
344 *error = "Schema must be a JSON object"; | |
345 return scoped_ptr<base::DictionaryValue>(); | |
346 } | |
347 if (!::IsValidSchema(dict, error)) | |
348 return scoped_ptr<base::DictionaryValue>(); | |
349 ignore_result(json.release()); | |
350 return make_scoped_ptr(dict); | |
351 } | |
352 | |
124 JSONSchemaValidator::JSONSchemaValidator(DictionaryValue* schema) | 353 JSONSchemaValidator::JSONSchemaValidator(DictionaryValue* schema) |
125 : schema_root_(schema), default_allow_additional_properties_(false) { | 354 : schema_root_(schema), default_allow_additional_properties_(false) { |
126 } | 355 } |
127 | 356 |
128 JSONSchemaValidator::JSONSchemaValidator(DictionaryValue* schema, | 357 JSONSchemaValidator::JSONSchemaValidator(DictionaryValue* schema, |
129 ListValue* types) | 358 ListValue* types) |
130 : schema_root_(schema), default_allow_additional_properties_(false) { | 359 : schema_root_(schema), default_allow_additional_properties_(false) { |
131 if (!types) | 360 if (!types) |
132 return; | 361 return; |
133 | 362 |
(...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
487 | 716 |
488 if (*additional_properties_schema) { | 717 if (*additional_properties_schema) { |
489 std::string additional_properties_type(schema::kAny); | 718 std::string additional_properties_type(schema::kAny); |
490 CHECK((*additional_properties_schema)->GetString( | 719 CHECK((*additional_properties_schema)->GetString( |
491 schema::kType, &additional_properties_type)); | 720 schema::kType, &additional_properties_type)); |
492 return additional_properties_type == schema::kAny; | 721 return additional_properties_type == schema::kAny; |
493 } else { | 722 } else { |
494 return default_allow_additional_properties_; | 723 return default_allow_additional_properties_; |
495 } | 724 } |
496 } | 725 } |
OLD | NEW |