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
3 // found in the LICENSE file.
5 #include "components/policy/core/common/schema.h"
9 #include "base/compiler_specific.h"
10 #include "base/logging.h"
11 #include "components/json_schema/json_schema_constants.h"
12 #include "components/json_schema/json_schema_validator.h"
13 #include "components/policy/core/common/schema_internal.h"
19 bool SchemaTypeToValueType(const std::string& type_string,
20 base::Value::Type* type) {
21 // Note: "any" is not an accepted type.
23 const char* schema_type;
24 base::Value::Type value_type;
25 } kSchemaToValueTypeMap[] = {
26 { json_schema_constants::kArray, base::Value::TYPE_LIST },
27 { json_schema_constants::kBoolean, base::Value::TYPE_BOOLEAN },
28 { json_schema_constants::kInteger, base::Value::TYPE_INTEGER },
29 { json_schema_constants::kNull, base::Value::TYPE_NULL },
30 { json_schema_constants::kNumber, base::Value::TYPE_DOUBLE },
31 { json_schema_constants::kObject, base::Value::TYPE_DICTIONARY },
32 { json_schema_constants::kString, base::Value::TYPE_STRING },
34 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSchemaToValueTypeMap); ++i) {
35 if (kSchemaToValueTypeMap[i].schema_type == type_string) {
36 *type = kSchemaToValueTypeMap[i].value_type;
45 Schema::Iterator::Iterator(const internal::PropertiesNode* properties)
46 : it_(properties->begin),
47 end_(properties->end) {}
49 Schema::Iterator::Iterator(const Iterator& iterator)
51 end_(iterator.end_) {}
53 Schema::Iterator::~Iterator() {}
55 Schema::Iterator& Schema::Iterator::operator=(const Iterator& iterator) {
61 bool Schema::Iterator::IsAtEnd() const {
65 void Schema::Iterator::Advance() {
69 const char* Schema::Iterator::key() const {
73 Schema Schema::Iterator::schema() const {
74 return Schema(it_->schema);
77 Schema::Schema() : schema_(NULL) {}
79 Schema::Schema(const internal::SchemaNode* schema) : schema_(schema) {}
81 Schema::Schema(const Schema& schema) : schema_(schema.schema_) {}
83 Schema& Schema::operator=(const Schema& schema) {
84 schema_ = schema.schema_;
88 base::Value::Type Schema::type() const {
93 Schema::Iterator Schema::GetPropertiesIterator() const {
95 CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
97 static_cast<const internal::PropertiesNode*>(schema_->extra));
102 bool CompareKeys(const internal::PropertyNode& node, const std::string& key) {
103 return node.key < key;
108 Schema Schema::GetKnownProperty(const std::string& key) const {
110 CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
111 const internal::PropertiesNode* properties_node =
112 static_cast<const internal::PropertiesNode*>(schema_->extra);
113 const internal::PropertyNode* it = std::lower_bound(
114 properties_node->begin, properties_node->end, key, CompareKeys);
115 if (it != properties_node->end && it->key == key)
116 return Schema(it->schema);
120 Schema Schema::GetAdditionalProperties() const {
122 CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
124 static_cast<const internal::PropertiesNode*>(schema_->extra)->additional);
127 Schema Schema::GetProperty(const std::string& key) const {
128 Schema schema = GetKnownProperty(key);
129 return schema.valid() ? schema : GetAdditionalProperties();
132 Schema Schema::GetItems() const {
134 CHECK_EQ(base::Value::TYPE_LIST, type());
135 return Schema(static_cast<const internal::SchemaNode*>(schema_->extra));
138 SchemaOwner::SchemaOwner(const internal::SchemaNode* root) : root_(root) {}
140 SchemaOwner::~SchemaOwner() {
141 for (size_t i = 0; i < property_nodes_.size(); ++i)
142 delete[] property_nodes_[i];
146 scoped_ptr<SchemaOwner> SchemaOwner::Wrap(const internal::SchemaNode* schema) {
147 return scoped_ptr<SchemaOwner>(new SchemaOwner(schema));
151 scoped_ptr<SchemaOwner> SchemaOwner::Parse(const std::string& content,
152 std::string* error) {
153 // Validate as a generic JSON schema.
154 scoped_ptr<base::DictionaryValue> dict =
155 JSONSchemaValidator::IsValidSchema(content, error);
157 return scoped_ptr<SchemaOwner>();
159 // Validate the main type.
160 std::string string_value;
161 if (!dict->GetString(json_schema_constants::kType, &string_value) ||
162 string_value != json_schema_constants::kObject) {
164 "The main schema must have a type attribute with \"object\" value.";
165 return scoped_ptr<SchemaOwner>();
168 // Checks for invalid attributes at the top-level.
169 if (dict->HasKey(json_schema_constants::kAdditionalProperties) ||
170 dict->HasKey(json_schema_constants::kPatternProperties)) {
171 *error = "\"additionalProperties\" and \"patternProperties\" are not "
172 "supported at the main schema.";
173 return scoped_ptr<SchemaOwner>();
176 scoped_ptr<SchemaOwner> impl(new SchemaOwner(NULL));
177 impl->root_ = impl->Parse(*dict, error);
180 return impl.PassAs<SchemaOwner>();
183 const internal::SchemaNode* SchemaOwner::Parse(
184 const base::DictionaryValue& schema,
185 std::string* error) {
186 std::string type_string;
187 if (!schema.GetString(json_schema_constants::kType, &type_string)) {
188 *error = "The schema type must be declared.";
192 base::Value::Type type = base::Value::TYPE_NULL;
193 if (!SchemaTypeToValueType(type_string, &type)) {
194 *error = "Type not supported: " + type_string;
198 if (type == base::Value::TYPE_DICTIONARY)
199 return ParseDictionary(schema, error);
200 if (type == base::Value::TYPE_LIST)
201 return ParseList(schema, error);
203 internal::SchemaNode* node = new internal::SchemaNode;
206 schema_nodes_.push_back(node);
210 const internal::SchemaNode* SchemaOwner::ParseDictionary(
211 const base::DictionaryValue& schema,
212 std::string* error) {
213 internal::PropertiesNode* properties_node = new internal::PropertiesNode;
214 properties_node->begin = NULL;
215 properties_node->end = NULL;
216 properties_node->additional = NULL;
217 properties_nodes_.push_back(properties_node);
219 const base::DictionaryValue* dict = NULL;
220 const base::DictionaryValue* properties = NULL;
221 if (schema.GetDictionary(json_schema_constants::kProperties, &properties)) {
222 internal::PropertyNode* property_nodes =
223 new internal::PropertyNode[properties->size()];
224 property_nodes_.push_back(property_nodes);
227 for (base::DictionaryValue::Iterator it(*properties);
228 !it.IsAtEnd(); it.Advance(), ++index) {
229 // This should have been verified by the JSONSchemaValidator.
230 CHECK(it.value().GetAsDictionary(&dict));
231 const internal::SchemaNode* sub_schema = Parse(*dict, error);
234 std::string* key = new std::string(it.key());
235 keys_.push_back(key);
236 property_nodes[index].key = key->c_str();
237 property_nodes[index].schema = sub_schema;
239 CHECK_EQ(properties->size(), index);
240 properties_node->begin = property_nodes;
241 properties_node->end = property_nodes + index;
244 if (schema.GetDictionary(json_schema_constants::kAdditionalProperties,
246 const internal::SchemaNode* sub_schema = Parse(*dict, error);
249 properties_node->additional = sub_schema;
252 internal::SchemaNode* schema_node = new internal::SchemaNode;
253 schema_node->type = base::Value::TYPE_DICTIONARY;
254 schema_node->extra = properties_node;
255 schema_nodes_.push_back(schema_node);
259 const internal::SchemaNode* SchemaOwner::ParseList(
260 const base::DictionaryValue& schema,
261 std::string* error) {
262 const base::DictionaryValue* dict = NULL;
263 if (!schema.GetDictionary(json_schema_constants::kItems, &dict)) {
264 *error = "Arrays must declare a single schema for their items.";
267 const internal::SchemaNode* items_schema_node = Parse(*dict, error);
268 if (!items_schema_node)
271 internal::SchemaNode* schema_node = new internal::SchemaNode;
272 schema_node->type = base::Value::TYPE_LIST;
273 schema_node->extra = items_schema_node;
274 schema_nodes_.push_back(schema_node);
278 } // namespace policy