Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(501)

Unified Diff: components/policy/core/common/schema.cc

Issue 23851022: Added new policy Schema classes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: POLICY_EXPORT for Schema::Iterator Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: components/policy/core/common/schema.cc
diff --git a/components/policy/core/common/schema.cc b/components/policy/core/common/schema.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d897228552da876cce4ee6cfd6a6fbebd6ce88c5
--- /dev/null
+++ b/components/policy/core/common/schema.cc
@@ -0,0 +1,332 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/policy/core/common/schema.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "components/json_schema/json_schema_constants.h"
+#include "components/json_schema/json_schema_validator.h"
+#include "components/policy/core/common/schema_internal.h"
+
+namespace policy {
+
+namespace schema {
+
+namespace {
+
+const char kJSONSchemaVersion[] = "http://json-schema.org/draft-03/schema#";
+
+class PolicySchemaImpl : public PolicySchema {
Mattias Nissler (ping if slow) 2013/09/13 12:56:48 If PolicySchema has only one implementation, why m
Joao da Silva 2013/09/13 14:02:54 Right. There were 2 implementations but in the end
+ public:
+ explicit PolicySchemaImpl(const internal::SchemaNode* root)
+ : root_(root) {}
+
+ virtual ~PolicySchemaImpl();
+
+ static scoped_ptr<PolicySchema> Parse(const std::string& schema,
+ std::string* error);
+
+ virtual Schema schema() const OVERRIDE {
+ return Schema(root_);
+ }
+
+ private:
+ PolicySchemaImpl() : root_(NULL) {}
+
+ const internal::SchemaNode* Parse(const base::DictionaryValue& schema,
+ std::string* error);
+ const internal::SchemaNode* ParseDictionary(
+ const base::DictionaryValue& schema,
+ std::string* error);
+ const internal::SchemaNode* ParseList(const base::DictionaryValue& schema,
+ std::string* error);
+
+ const internal::SchemaNode* root_;
+ ScopedVector<internal::SchemaNode> schema_nodes_;
+ // Note: |property_nodes_| contains PropertyNode[] and must be cleared
+ // manually to properly use delete[].
+ std::vector<internal::PropertyNode*> property_nodes_;
+ ScopedVector<internal::PropertiesNode> properties_nodes_;
+ ScopedVector<std::string> keys_;
+
+ DISALLOW_COPY_AND_ASSIGN(PolicySchemaImpl);
+};
+
+bool SchemaTypeToValueType(const std::string& type_string,
+ base::Value::Type* type) {
+ // Note: "any" is not an accepted type.
+ static const struct {
+ const char* schema_type;
+ base::Value::Type value_type;
+ } kSchemaToValueTypeMap[] = {
+ { json_schema_constants::kArray, base::Value::TYPE_LIST },
+ { json_schema_constants::kBoolean, base::Value::TYPE_BOOLEAN },
+ { json_schema_constants::kInteger, base::Value::TYPE_INTEGER },
+ { json_schema_constants::kNull, base::Value::TYPE_NULL },
+ { json_schema_constants::kNumber, base::Value::TYPE_DOUBLE },
+ { json_schema_constants::kObject, base::Value::TYPE_DICTIONARY },
+ { json_schema_constants::kString, base::Value::TYPE_STRING },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSchemaToValueTypeMap); ++i) {
+ if (kSchemaToValueTypeMap[i].schema_type == type_string) {
+ *type = kSchemaToValueTypeMap[i].value_type;
+ return true;
+ }
+ }
+ return false;
+}
+
+PolicySchemaImpl::~PolicySchemaImpl() {
+ for (size_t i = 0; i < property_nodes_.size(); ++i)
+ delete[] property_nodes_[i];
+}
+
+// static
+scoped_ptr<PolicySchema> PolicySchemaImpl::Parse(const std::string& content,
+ std::string* error) {
+ // Validate as a generic JSON schema.
+ scoped_ptr<base::DictionaryValue> dict =
+ JSONSchemaValidator::IsValidSchema(content, error);
+ if (!dict)
+ return scoped_ptr<PolicySchema>();
+
+ // Validate the schema version.
Mattias Nissler (ping if slow) 2013/09/13 12:56:48 That means we can never migrate to newer schema ve
Joao da Silva 2013/09/13 14:02:54 What can we possibly do for old builds? We'll hav
Mattias Nissler (ping if slow) 2013/09/13 14:55:17 I'm worried about the fact that extension authors
Joao da Silva 2013/09/13 15:15:47 I see where you're coming from. The latest draft i
+ std::string string_value;
+ if (!dict->GetString(json_schema_constants::kSchema, &string_value) ||
+ string_value != kJSONSchemaVersion) {
+ *error = "Must declare JSON Schema v3 version in \"$schema\".";
+ return scoped_ptr<PolicySchema>();
+ }
+
+ // Validate the main type.
+ if (!dict->GetString(json_schema_constants::kType, &string_value) ||
+ string_value != json_schema_constants::kObject) {
+ *error =
+ "The main schema must have a type attribute with \"object\" value.";
+ return scoped_ptr<PolicySchema>();
+ }
+
+ // Checks for invalid attributes at the top-level.
Mattias Nissler (ping if slow) 2013/09/13 12:56:48 Any good reason? They'd just be ignored.
Joao da Silva 2013/09/13 14:02:54 So that extension authors see the error. These co
Mattias Nissler (ping if slow) 2013/09/13 14:55:17 Right, makes sense. Let's leave the check in then.
+ if (dict->HasKey(json_schema_constants::kAdditionalProperties) ||
+ dict->HasKey(json_schema_constants::kPatternProperties)) {
+ *error = "\"additionalProperties\" and \"patternProperties\" are not "
+ "supported at the main schema.";
+ return scoped_ptr<PolicySchema>();
+ }
+
+ scoped_ptr<PolicySchemaImpl> impl(new PolicySchemaImpl());
+ impl->root_ = impl->Parse(*dict, error);
+ if (!impl->root_)
+ impl.reset();
+ return impl.PassAs<PolicySchema>();
+}
+
+const internal::SchemaNode* PolicySchemaImpl::Parse(
+ const base::DictionaryValue& schema,
+ std::string* error) {
+ std::string type_string;
+ if (!schema.GetString(json_schema_constants::kType, &type_string)) {
+ *error = "The schema type must be declared.";
+ return NULL;
+ }
+
+ base::Value::Type type = base::Value::TYPE_NULL;
+ if (!SchemaTypeToValueType(type_string, &type)) {
+ *error = "The \"any\" type can't be used.";
Mattias Nissler (ping if slow) 2013/09/13 12:56:48 I think you should just say "Type not supported: "
Joao da Silva 2013/09/13 14:02:54 Done. The "any" type was assumed because the JSONS
+ return NULL;
+ }
+
+ if (type == base::Value::TYPE_DICTIONARY)
+ return ParseDictionary(schema, error);
+ if (type == base::Value::TYPE_LIST)
+ return ParseList(schema, error);
+
+ internal::SchemaNode* node = new internal::SchemaNode;
+ node->type = type;
+ node->extra = NULL;
+ schema_nodes_.push_back(node);
+ return node;
+}
+
+const internal::SchemaNode* PolicySchemaImpl::ParseDictionary(
+ const base::DictionaryValue& schema,
+ std::string* error) {
+ internal::PropertiesNode* properties_node = new internal::PropertiesNode;
+ properties_node->begin = NULL;
+ properties_node->end = NULL;
+ properties_node->additional = NULL;
+ properties_nodes_.push_back(properties_node);
+
+ const base::DictionaryValue* dict = NULL;
+ const base::DictionaryValue* properties = NULL;
+ if (schema.GetDictionary(json_schema_constants::kProperties, &properties)) {
+ internal::PropertyNode* property_nodes =
+ new internal::PropertyNode[properties->size()];
+ property_nodes_.push_back(property_nodes);
+
+ size_t index = 0;
+ for (base::DictionaryValue::Iterator it(*properties);
+ !it.IsAtEnd(); it.Advance(), ++index) {
+ // This should have been verified by the JSONSchemaValidator.
+ CHECK(it.value().GetAsDictionary(&dict));
+ const internal::SchemaNode* sub_schema = Parse(*dict, error);
+ if (!sub_schema)
+ return NULL;
+ std::string* key = new std::string(it.key());
+ keys_.push_back(key);
+ property_nodes[index].key = key->c_str();
+ property_nodes[index].schema = sub_schema;
+ }
+ CHECK_EQ(properties->size(), index);
+ properties_node->begin = property_nodes;
+ properties_node->end = property_nodes + index;
+ }
+
+ if (schema.GetDictionary(json_schema_constants::kAdditionalProperties,
+ &dict)) {
+ const internal::SchemaNode* sub_schema = Parse(*dict, error);
+ if (!sub_schema)
+ return NULL;
+ properties_node->additional = sub_schema;
+ }
+
+ internal::SchemaNode* schema_node = new internal::SchemaNode;
+ schema_node->type = base::Value::TYPE_DICTIONARY;
+ schema_node->extra = properties_node;
+ schema_nodes_.push_back(schema_node);
+ return schema_node;
+}
+
+const internal::SchemaNode* PolicySchemaImpl::ParseList(
+ const base::DictionaryValue& schema,
+ std::string* error) {
+ const base::DictionaryValue* dict = NULL;
+ if (!schema.GetDictionary(json_schema_constants::kItems, &dict)) {
+ *error = "Arrays must declare a single schema for their items.";
+ return NULL;
+ }
+ const internal::SchemaNode* items_schema_node = Parse(*dict, error);
+ if (!items_schema_node)
+ return NULL;
+
+ internal::SchemaNode* schema_node = new internal::SchemaNode;
+ schema_node->type = base::Value::TYPE_LIST;
+ schema_node->extra = items_schema_node;
+ schema_nodes_.push_back(schema_node);
+ return schema_node;
+}
+
+} // namespace
+
+Schema::Iterator::Iterator(const internal::PropertiesNode* properties)
+ : it_(properties->begin),
+ end_(properties->end) {}
+
+Schema::Iterator::Iterator(const Iterator& iterator)
+ : it_(iterator.it_),
+ end_(iterator.end_) {}
+
+Schema::Iterator::~Iterator() {}
+
+Schema::Iterator& Schema::Iterator::operator=(const Iterator& iterator) {
+ it_ = iterator.it_;
+ end_ = iterator.end_;
+ return *this;
+}
+
+bool Schema::Iterator::IsAtEnd() const {
+ return it_ == end_;
+}
+
+void Schema::Iterator::Advance() {
+ ++it_;
+}
+
+const char* Schema::Iterator::key() const {
+ return it_->key;
+}
+
+Schema Schema::Iterator::schema() const {
+ return Schema(it_->schema);
+}
+
+Schema::Schema(const internal::SchemaNode* schema) : schema_(schema) {}
+
+Schema::Schema(const Schema& schema) : schema_(schema.schema_) {}
+
+Schema& Schema::operator=(const Schema& schema) {
+ schema_ = schema.schema_;
+ return *this;
+}
+
+base::Value::Type Schema::type() const {
+ CHECK(valid());
+ return schema_->type;
+}
+
+Schema::Iterator Schema::GetPropertiesIterator() const {
+ CHECK(valid());
+ CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
+ return Iterator(
+ static_cast<const internal::PropertiesNode*>(schema_->extra));
+}
+
+namespace {
+
+bool CompareKeys(const internal::PropertyNode& node, const std::string& key) {
+ return node.key < key;
+}
+
+} // namespace
+
+Schema Schema::GetKnownProperty(const std::string& key) const {
+ CHECK(valid());
+ CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
+ const internal::PropertiesNode* properties_node =
+ static_cast<const internal::PropertiesNode*>(schema_->extra);
+ const internal::PropertyNode* it = std::lower_bound(
+ properties_node->begin, properties_node->end, key, CompareKeys);
+ if (it != properties_node->end && it->key == key)
+ return Schema(it->schema);
+ return Schema(NULL);
+}
+
+Schema Schema::GetAdditionalProperties() const {
+ CHECK(valid());
+ CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
+ return Schema(
+ static_cast<const internal::PropertiesNode*>(schema_->extra)->additional);
Mattias Nissler (ping if slow) 2013/09/13 12:56:48 Hm, casts. Maybe use a union? That's still easier
Joao da Silva 2013/09/13 14:02:54 I don't mind these casts *at all* :-) I did want
Mattias Nissler (ping if slow) 2013/09/13 14:55:17 Fine to go with the cast then.
+}
+
+Schema Schema::GetProperty(const std::string& key) const {
+ Schema schema = GetKnownProperty(key);
+ return schema.valid() ? schema : GetAdditionalProperties();
Mattias Nissler (ping if slow) 2013/09/13 12:56:48 Shouldn't this return Schema(NULL) for the error c
Joao da Silva 2013/09/13 14:02:54 GetAdditionalProperties returns Schema(NULL) if th
Mattias Nissler (ping if slow) 2013/09/13 14:55:17 Right, sorry, I was confused.
+}
+
+Schema Schema::GetItems() const {
+ CHECK(valid());
+ CHECK_EQ(base::Value::TYPE_LIST, type());
+ return Schema(static_cast<const internal::SchemaNode*>(schema_->extra));
+}
+
+// static
+scoped_ptr<PolicySchema> PolicySchema::Wrap(
+ const internal::SchemaNode* schema) {
+ return scoped_ptr<PolicySchema>(new PolicySchemaImpl(schema));
+}
+
+// static
+scoped_ptr<PolicySchema> PolicySchema::Parse(const std::string& schema,
+ std::string* error) {
+ return PolicySchemaImpl::Parse(schema, error);
+}
+
+} // namespace schema
+
+} // namespace policy

Powered by Google App Engine
This is Rietveld 408576698