- add sources.
[platform/framework/web/crosswalk.git] / src / components / policy / core / common / schema.cc
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.
4
5 #include "components/policy/core/common/schema.h"
6
7 #include <algorithm>
8
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"
14
15 namespace policy {
16
17 namespace {
18
19 bool SchemaTypeToValueType(const std::string& type_string,
20                            base::Value::Type* type) {
21   // Note: "any" is not an accepted type.
22   static const struct {
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     },
33   };
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;
37       return true;
38     }
39   }
40   return false;
41 }
42
43 }  // namespace
44
45 Schema::Iterator::Iterator(const internal::PropertiesNode* properties)
46     : it_(properties->begin),
47       end_(properties->end) {}
48
49 Schema::Iterator::Iterator(const Iterator& iterator)
50     : it_(iterator.it_),
51       end_(iterator.end_) {}
52
53 Schema::Iterator::~Iterator() {}
54
55 Schema::Iterator& Schema::Iterator::operator=(const Iterator& iterator) {
56   it_ = iterator.it_;
57   end_ = iterator.end_;
58   return *this;
59 }
60
61 bool Schema::Iterator::IsAtEnd() const {
62   return it_ == end_;
63 }
64
65 void Schema::Iterator::Advance() {
66   ++it_;
67 }
68
69 const char* Schema::Iterator::key() const {
70   return it_->key;
71 }
72
73 Schema Schema::Iterator::schema() const {
74   return Schema(it_->schema);
75 }
76
77 Schema::Schema() : schema_(NULL) {}
78
79 Schema::Schema(const internal::SchemaNode* schema) : schema_(schema) {}
80
81 Schema::Schema(const Schema& schema) : schema_(schema.schema_) {}
82
83 Schema& Schema::operator=(const Schema& schema) {
84   schema_ = schema.schema_;
85   return *this;
86 }
87
88 base::Value::Type Schema::type() const {
89   CHECK(valid());
90   return schema_->type;
91 }
92
93 Schema::Iterator Schema::GetPropertiesIterator() const {
94   CHECK(valid());
95   CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
96   return Iterator(
97       static_cast<const internal::PropertiesNode*>(schema_->extra));
98 }
99
100 namespace {
101
102 bool CompareKeys(const internal::PropertyNode& node, const std::string& key) {
103   return node.key < key;
104 }
105
106 }  // namespace
107
108 Schema Schema::GetKnownProperty(const std::string& key) const {
109   CHECK(valid());
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);
117   return Schema();
118 }
119
120 Schema Schema::GetAdditionalProperties() const {
121   CHECK(valid());
122   CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
123   return Schema(
124       static_cast<const internal::PropertiesNode*>(schema_->extra)->additional);
125 }
126
127 Schema Schema::GetProperty(const std::string& key) const {
128   Schema schema = GetKnownProperty(key);
129   return schema.valid() ? schema : GetAdditionalProperties();
130 }
131
132 Schema Schema::GetItems() const {
133   CHECK(valid());
134   CHECK_EQ(base::Value::TYPE_LIST, type());
135   return Schema(static_cast<const internal::SchemaNode*>(schema_->extra));
136 }
137
138 SchemaOwner::SchemaOwner(const internal::SchemaNode* root) : root_(root) {}
139
140 SchemaOwner::~SchemaOwner() {
141   for (size_t i = 0; i < property_nodes_.size(); ++i)
142     delete[] property_nodes_[i];
143 }
144
145 // static
146 scoped_ptr<SchemaOwner> SchemaOwner::Wrap(const internal::SchemaNode* schema) {
147   return scoped_ptr<SchemaOwner>(new SchemaOwner(schema));
148 }
149
150 // static
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);
156   if (!dict)
157     return scoped_ptr<SchemaOwner>();
158
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) {
163     *error =
164         "The main schema must have a type attribute with \"object\" value.";
165     return scoped_ptr<SchemaOwner>();
166   }
167
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>();
174   }
175
176   scoped_ptr<SchemaOwner> impl(new SchemaOwner(NULL));
177   impl->root_ = impl->Parse(*dict, error);
178   if (!impl->root_)
179     impl.reset();
180   return impl.PassAs<SchemaOwner>();
181 }
182
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.";
189     return NULL;
190   }
191
192   base::Value::Type type = base::Value::TYPE_NULL;
193   if (!SchemaTypeToValueType(type_string, &type)) {
194     *error = "Type not supported: " + type_string;
195     return NULL;
196   }
197
198   if (type == base::Value::TYPE_DICTIONARY)
199     return ParseDictionary(schema, error);
200   if (type == base::Value::TYPE_LIST)
201     return ParseList(schema, error);
202
203   internal::SchemaNode* node = new internal::SchemaNode;
204   node->type = type;
205   node->extra = NULL;
206   schema_nodes_.push_back(node);
207   return node;
208 }
209
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);
218
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);
225
226     size_t index = 0;
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);
232       if (!sub_schema)
233         return NULL;
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;
238     }
239     CHECK_EQ(properties->size(), index);
240     properties_node->begin = property_nodes;
241     properties_node->end = property_nodes + index;
242   }
243
244   if (schema.GetDictionary(json_schema_constants::kAdditionalProperties,
245                            &dict)) {
246     const internal::SchemaNode* sub_schema = Parse(*dict, error);
247     if (!sub_schema)
248       return NULL;
249     properties_node->additional = sub_schema;
250   }
251
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);
256   return schema_node;
257 }
258
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.";
265     return NULL;
266   }
267   const internal::SchemaNode* items_schema_node = Parse(*dict, error);
268   if (!items_schema_node)
269     return NULL;
270
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);
275   return schema_node;
276 }
277
278 }  // namespace policy