blob: cef5030685425a9d4505a1456013fc277b938b12 [file] [log] [blame]
Alex Vakulenko66ec2922014-06-17 15:30:22 -07001// Copyright 2014 The Chromium OS 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 "buffet/commands/schema_utils.h"
6
7#include <algorithm>
8#include <set>
9#include <string>
10
11#include <base/json/json_writer.h>
12
13#include "buffet/commands/object_schema.h"
14#include "buffet/commands/prop_types.h"
15#include "buffet/commands/prop_values.h"
16
17namespace buffet {
18namespace {
19// Helper function to report "type mismatch" errors when parsing JSON.
20void ReportJsonTypeMismatch(const base::Value* value_in,
21 const std::string& expected_type,
22 ErrorPtr* error) {
23 std::string value_as_string;
24 base::JSONWriter::Write(value_in, &value_as_string);
Alex Vakulenko9ac962f2014-07-22 07:34:56 -070025 Error::AddToPrintf(error, errors::commands::kDomain,
26 errors::commands::kTypeMismatch,
Alex Vakulenko66ec2922014-06-17 15:30:22 -070027 "Unable to convert value %s into %s",
28 value_as_string.c_str(), expected_type.c_str());
29}
30
31// Template version of ReportJsonTypeMismatch that deduces the type of expected
32// data from the value_out parameter passed to particular overload of
33// TypedValueFromJson() function. Always returns false.
34template<typename T>
35bool ReportUnexpectedJson(const base::Value* value_in, T*, ErrorPtr* error) {
36 ReportJsonTypeMismatch(value_in,
37 PropType::GetTypeStringFromType(GetValueType<T>()),
38 error);
39 return false;
40}
41
42bool ErrorMissingProperty(ErrorPtr* error, const char* param_name) {
Alex Vakulenko9ac962f2014-07-22 07:34:56 -070043 Error::AddToPrintf(error, errors::commands::kDomain,
44 errors::commands::kPropertyMissing,
Alex Vakulenko66ec2922014-06-17 15:30:22 -070045 "Required parameter missing: %s", param_name);
46 return false;
47}
48} // namespace
49
50// Specializations of TypedValueToJson<T>() for supported C++ types.
51std::unique_ptr<base::Value> TypedValueToJson(bool value, ErrorPtr* error) {
52 return std::unique_ptr<base::Value>(base::Value::CreateBooleanValue(value));
53}
54
55std::unique_ptr<base::Value> TypedValueToJson(int value, ErrorPtr* error) {
56 return std::unique_ptr<base::Value>(base::Value::CreateIntegerValue(value));
57}
58
59std::unique_ptr<base::Value> TypedValueToJson(double value, ErrorPtr* error) {
60 return std::unique_ptr<base::Value>(base::Value::CreateDoubleValue(value));
61}
62
63std::unique_ptr<base::Value> TypedValueToJson(const std::string& value,
64 ErrorPtr* error) {
65 return std::unique_ptr<base::Value>(base::Value::CreateStringValue(value));
66}
67
68std::unique_ptr<base::Value> TypedValueToJson(const native_types::Object& value,
69 ErrorPtr* error) {
70 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
71 for (const auto& pair : value) {
72 auto prop_value = pair.second->ToJson(error);
73 if (!prop_value)
74 return prop_value;
75 dict->SetWithoutPathExpansion(pair.first, prop_value.release());
76 }
77 return std::move(dict);
78}
79
80bool TypedValueFromJson(const base::Value* value_in,
81 const ObjectSchema* object_schema,
82 bool* value_out, ErrorPtr* error) {
83 return value_in->GetAsBoolean(value_out) ||
84 ReportUnexpectedJson(value_in, value_out, error);
85}
86
87bool TypedValueFromJson(const base::Value* value_in,
88 const ObjectSchema* object_schema,
89 int* value_out, ErrorPtr* error) {
90 return value_in->GetAsInteger(value_out) ||
91 ReportUnexpectedJson(value_in, value_out, error);
92}
93
94bool TypedValueFromJson(const base::Value* value_in,
95 const ObjectSchema* object_schema,
96 double* value_out, ErrorPtr* error) {
97 return value_in->GetAsDouble(value_out) ||
98 ReportUnexpectedJson(value_in, value_out, error);
99}
100
101bool TypedValueFromJson(const base::Value* value_in,
102 const ObjectSchema* object_schema,
103 std::string* value_out, ErrorPtr* error) {
104 return value_in->GetAsString(value_out) ||
105 ReportUnexpectedJson(value_in, value_out, error);
106}
107
108bool TypedValueFromJson(const base::Value* value_in,
109 const ObjectSchema* object_schema,
110 native_types::Object* value_out, ErrorPtr* error) {
111 const base::DictionaryValue* dict = nullptr;
112 if (!value_in->GetAsDictionary(&dict))
113 return ReportUnexpectedJson(value_in, value_out, error);
114
115 CHECK(object_schema) << "Object schema must be provided";
116
117 std::set<std::string> keys_processed;
118 for (const auto& pair : object_schema->GetProps()) {
119 const PropValue* def_value = pair.second->GetDefaultValue();
120 if (dict->HasKey(pair.first)) {
121 std::shared_ptr<PropValue> value = pair.second->CreateValue();
122 const base::Value* param_value = nullptr;
123 CHECK(dict->GetWithoutPathExpansion(pair.first, &param_value))
124 << "Unable to get parameter";
125 if (!value->FromJson(param_value, error))
126 return false;
127 value_out->insert(std::make_pair(pair.first, std::move(value)));
128 } else if (def_value) {
129 std::shared_ptr<PropValue> value = def_value->Clone();
130 value_out->insert(std::make_pair(pair.first, std::move(value)));
131 } else {
132 return ErrorMissingProperty(error, pair.first.c_str());
133 }
134 keys_processed.insert(pair.first);
135 }
136
137 // Just for sanity, make sure that we processed all the necessary properties
138 // and there weren't any extra (unknown) ones specified. If so, ignore
139 // them, but log as warnings...
140 base::DictionaryValue::Iterator iter(*dict);
141 while (!iter.IsAtEnd()) {
142 std::string key = iter.key();
143 if (keys_processed.find(key) == keys_processed.end() &&
144 !object_schema->GetExtraPropertiesAllowed()) {
Alex Vakulenko9ac962f2014-07-22 07:34:56 -0700145 Error::AddToPrintf(error, errors::commands::kDomain,
146 errors::commands::kUnknownProperty,
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700147 "Unrecognized parameter '%s'", key.c_str());
148 return false;
149 }
150 iter.Advance();
151 }
152
153 // Now go over all property values and validate them.
154 for (const auto& pair : *value_out) {
155 const PropType* prop_type = pair.second->GetPropType();
156 CHECK(prop_type) << "Value property type must be available";
157 if (!prop_type->ValidateConstraints(*pair.second, error)) {
Alex Vakulenko9ac962f2014-07-22 07:34:56 -0700158 Error::AddToPrintf(error, errors::commands::kDomain,
159 errors::commands::kInvalidPropValue,
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700160 "Invalid parameter value for property '%s'",
161 pair.first.c_str());
162 return false;
163 }
164 }
165 return true;
166}
167
168// Compares two sets of key-value pairs from two Objects.
169static bool obj_cmp(const native_types::Object::value_type& v1,
170 const native_types::Object::value_type& v2) {
171 return (v1.first == v2.first) && v1.second->IsEqual(v2.second.get());
172}
173
174bool operator==(const native_types::Object& obj1,
175 const native_types::Object& obj2) {
176 if (obj1.size() != obj2.size())
177 return false;
178
179 auto pair = std::mismatch(obj1.begin(), obj1.end(), obj2.begin(), obj_cmp);
180 return pair == std::make_pair(obj1.end(), obj2.end());
181}
182
183std::string ToString(const native_types::Object& obj) {
184 auto val = TypedValueToJson(obj, nullptr);
185 std::string str;
186 base::JSONWriter::Write(val.get(), &str);
187 return str;
188}
189
190
191} // namespace buffet