Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 1 | // 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 | |
| 17 | namespace buffet { |
| 18 | namespace { |
| 19 | // Helper function to report "type mismatch" errors when parsing JSON. |
| 20 | void ReportJsonTypeMismatch(const base::Value* value_in, |
| 21 | const std::string& expected_type, |
Alex Vakulenko | 5f47206 | 2014-08-14 17:54:04 -0700 | [diff] [blame] | 22 | chromeos::ErrorPtr* error) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 23 | std::string value_as_string; |
| 24 | base::JSONWriter::Write(value_in, &value_as_string); |
Alex Vakulenko | ac8037d | 2014-11-11 11:42:05 -0800 | [diff] [blame] | 25 | chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, |
Alex Vakulenko | 5f47206 | 2014-08-14 17:54:04 -0700 | [diff] [blame] | 26 | errors::commands::kTypeMismatch, |
| 27 | "Unable to convert value %s into %s", |
| 28 | value_as_string.c_str(), expected_type.c_str()); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 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. |
| 34 | template<typename T> |
Alex Vakulenko | 5f47206 | 2014-08-14 17:54:04 -0700 | [diff] [blame] | 35 | bool ReportUnexpectedJson(const base::Value* value_in, T*, |
| 36 | chromeos::ErrorPtr* error) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 37 | ReportJsonTypeMismatch(value_in, |
| 38 | PropType::GetTypeStringFromType(GetValueType<T>()), |
| 39 | error); |
| 40 | return false; |
| 41 | } |
| 42 | |
Alex Vakulenko | 5f47206 | 2014-08-14 17:54:04 -0700 | [diff] [blame] | 43 | bool ErrorMissingProperty(chromeos::ErrorPtr* error, const char* param_name) { |
Alex Vakulenko | ac8037d | 2014-11-11 11:42:05 -0800 | [diff] [blame] | 44 | chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, |
Alex Vakulenko | 5f47206 | 2014-08-14 17:54:04 -0700 | [diff] [blame] | 45 | errors::commands::kPropertyMissing, |
| 46 | "Required parameter missing: %s", param_name); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 47 | return false; |
| 48 | } |
| 49 | } // namespace |
| 50 | |
| 51 | // Specializations of TypedValueToJson<T>() for supported C++ types. |
Alex Vakulenko | 5f47206 | 2014-08-14 17:54:04 -0700 | [diff] [blame] | 52 | std::unique_ptr<base::Value> TypedValueToJson(bool value, |
| 53 | chromeos::ErrorPtr* error) { |
Ben Chan | d0ce0d7 | 2014-09-04 21:59:24 -0700 | [diff] [blame] | 54 | return std::unique_ptr<base::Value>(new base::FundamentalValue(value)); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 55 | } |
| 56 | |
Alex Vakulenko | 5f47206 | 2014-08-14 17:54:04 -0700 | [diff] [blame] | 57 | std::unique_ptr<base::Value> TypedValueToJson(int value, |
| 58 | chromeos::ErrorPtr* error) { |
Ben Chan | d0ce0d7 | 2014-09-04 21:59:24 -0700 | [diff] [blame] | 59 | return std::unique_ptr<base::Value>(new base::FundamentalValue(value)); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 60 | } |
| 61 | |
Alex Vakulenko | 5f47206 | 2014-08-14 17:54:04 -0700 | [diff] [blame] | 62 | std::unique_ptr<base::Value> TypedValueToJson(double value, |
| 63 | chromeos::ErrorPtr* error) { |
Ben Chan | d0ce0d7 | 2014-09-04 21:59:24 -0700 | [diff] [blame] | 64 | return std::unique_ptr<base::Value>(new base::FundamentalValue(value)); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 65 | } |
| 66 | |
| 67 | std::unique_ptr<base::Value> TypedValueToJson(const std::string& value, |
Alex Vakulenko | 5f47206 | 2014-08-14 17:54:04 -0700 | [diff] [blame] | 68 | chromeos::ErrorPtr* error) { |
Ben Chan | d0ce0d7 | 2014-09-04 21:59:24 -0700 | [diff] [blame] | 69 | return std::unique_ptr<base::Value>(new base::StringValue(value)); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 70 | } |
| 71 | |
| 72 | std::unique_ptr<base::Value> TypedValueToJson(const native_types::Object& value, |
Alex Vakulenko | 5f47206 | 2014-08-14 17:54:04 -0700 | [diff] [blame] | 73 | chromeos::ErrorPtr* error) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 74 | std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue); |
| 75 | for (const auto& pair : value) { |
| 76 | auto prop_value = pair.second->ToJson(error); |
| 77 | if (!prop_value) |
| 78 | return prop_value; |
| 79 | dict->SetWithoutPathExpansion(pair.first, prop_value.release()); |
| 80 | } |
| 81 | return std::move(dict); |
| 82 | } |
| 83 | |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 84 | std::unique_ptr<base::Value> TypedValueToJson(const native_types::Array& value, |
| 85 | chromeos::ErrorPtr* error) { |
| 86 | std::unique_ptr<base::ListValue> list(new base::ListValue); |
| 87 | for (const auto& item : value) { |
| 88 | auto json = item->ToJson(error); |
| 89 | if (!json) |
| 90 | return std::unique_ptr<base::Value>(); |
| 91 | list->Append(json.release()); |
| 92 | } |
| 93 | return std::move(list); |
| 94 | } |
| 95 | |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 96 | bool TypedValueFromJson(const base::Value* value_in, |
Alex Vakulenko | d94656e | 2015-03-18 09:54:37 -0700 | [diff] [blame] | 97 | const PropType* type, |
| 98 | bool* value_out, |
| 99 | chromeos::ErrorPtr* error) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 100 | return value_in->GetAsBoolean(value_out) || |
| 101 | ReportUnexpectedJson(value_in, value_out, error); |
| 102 | } |
| 103 | |
| 104 | bool TypedValueFromJson(const base::Value* value_in, |
Alex Vakulenko | d94656e | 2015-03-18 09:54:37 -0700 | [diff] [blame] | 105 | const PropType* type, |
| 106 | int* value_out, |
| 107 | chromeos::ErrorPtr* error) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 108 | return value_in->GetAsInteger(value_out) || |
| 109 | ReportUnexpectedJson(value_in, value_out, error); |
| 110 | } |
| 111 | |
| 112 | bool TypedValueFromJson(const base::Value* value_in, |
Alex Vakulenko | d94656e | 2015-03-18 09:54:37 -0700 | [diff] [blame] | 113 | const PropType* type, |
| 114 | double* value_out, |
| 115 | chromeos::ErrorPtr* error) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 116 | return value_in->GetAsDouble(value_out) || |
| 117 | ReportUnexpectedJson(value_in, value_out, error); |
| 118 | } |
| 119 | |
| 120 | bool TypedValueFromJson(const base::Value* value_in, |
Alex Vakulenko | d94656e | 2015-03-18 09:54:37 -0700 | [diff] [blame] | 121 | const PropType* type, |
| 122 | std::string* value_out, |
| 123 | chromeos::ErrorPtr* error) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 124 | return value_in->GetAsString(value_out) || |
| 125 | ReportUnexpectedJson(value_in, value_out, error); |
| 126 | } |
| 127 | |
| 128 | bool TypedValueFromJson(const base::Value* value_in, |
Alex Vakulenko | d94656e | 2015-03-18 09:54:37 -0700 | [diff] [blame] | 129 | const PropType* type, |
Alex Vakulenko | 5f47206 | 2014-08-14 17:54:04 -0700 | [diff] [blame] | 130 | native_types::Object* value_out, |
| 131 | chromeos::ErrorPtr* error) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 132 | const base::DictionaryValue* dict = nullptr; |
| 133 | if (!value_in->GetAsDictionary(&dict)) |
| 134 | return ReportUnexpectedJson(value_in, value_out, error); |
| 135 | |
Alex Vakulenko | d94656e | 2015-03-18 09:54:37 -0700 | [diff] [blame] | 136 | CHECK(type) << "Object definition must be provided"; |
| 137 | CHECK(ValueType::Object == type->GetType()) << "Type must be Object"; |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 138 | |
Alex Vakulenko | d94656e | 2015-03-18 09:54:37 -0700 | [diff] [blame] | 139 | const ObjectSchema* object_schema = type->GetObject()->GetObjectSchemaPtr(); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 140 | std::set<std::string> keys_processed; |
Alex Vakulenko | 3e864c0 | 2015-03-24 10:19:32 -0700 | [diff] [blame] | 141 | value_out->clear(); // Clear possible default values already in |value_out|. |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 142 | for (const auto& pair : object_schema->GetProps()) { |
| 143 | const PropValue* def_value = pair.second->GetDefaultValue(); |
| 144 | if (dict->HasKey(pair.first)) { |
Alex Vakulenko | 5ef7579 | 2015-03-19 15:50:44 -0700 | [diff] [blame] | 145 | auto value = pair.second->CreateValue(); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 146 | const base::Value* param_value = nullptr; |
| 147 | CHECK(dict->GetWithoutPathExpansion(pair.first, ¶m_value)) |
| 148 | << "Unable to get parameter"; |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 149 | if (!value->FromJson(param_value, error) || |
| 150 | !pair.second->ValidateValue(value->GetValueAsAny(), error)) { |
Alex Vakulenko | ac8037d | 2014-11-11 11:42:05 -0800 | [diff] [blame] | 151 | chromeos::Error::AddToPrintf(error, FROM_HERE, |
| 152 | errors::commands::kDomain, |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 153 | errors::commands::kInvalidPropValue, |
| 154 | "Invalid value for property '%s'", |
| 155 | pair.first.c_str()); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 156 | return false; |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 157 | } |
Alex Vakulenko | 3e864c0 | 2015-03-24 10:19:32 -0700 | [diff] [blame] | 158 | value_out->emplace_hint(value_out->end(), pair.first, std::move(value)); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 159 | } else if (def_value) { |
Alex Vakulenko | 3e864c0 | 2015-03-24 10:19:32 -0700 | [diff] [blame] | 160 | value_out->emplace_hint(value_out->end(), pair.first, def_value->Clone()); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 161 | } else { |
| 162 | return ErrorMissingProperty(error, pair.first.c_str()); |
| 163 | } |
| 164 | keys_processed.insert(pair.first); |
| 165 | } |
| 166 | |
| 167 | // Just for sanity, make sure that we processed all the necessary properties |
| 168 | // and there weren't any extra (unknown) ones specified. If so, ignore |
| 169 | // them, but log as warnings... |
| 170 | base::DictionaryValue::Iterator iter(*dict); |
| 171 | while (!iter.IsAtEnd()) { |
| 172 | std::string key = iter.key(); |
| 173 | if (keys_processed.find(key) == keys_processed.end() && |
| 174 | !object_schema->GetExtraPropertiesAllowed()) { |
Alex Vakulenko | ac8037d | 2014-11-11 11:42:05 -0800 | [diff] [blame] | 175 | chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, |
Alex Vakulenko | 5f47206 | 2014-08-14 17:54:04 -0700 | [diff] [blame] | 176 | errors::commands::kUnknownProperty, |
| 177 | "Unrecognized parameter '%s'", key.c_str()); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 178 | return false; |
| 179 | } |
| 180 | iter.Advance(); |
| 181 | } |
| 182 | |
| 183 | // Now go over all property values and validate them. |
| 184 | for (const auto& pair : *value_out) { |
| 185 | const PropType* prop_type = pair.second->GetPropType(); |
| 186 | CHECK(prop_type) << "Value property type must be available"; |
| 187 | if (!prop_type->ValidateConstraints(*pair.second, error)) { |
Alex Vakulenko | ac8037d | 2014-11-11 11:42:05 -0800 | [diff] [blame] | 188 | chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, |
Alex Vakulenko | 5f47206 | 2014-08-14 17:54:04 -0700 | [diff] [blame] | 189 | errors::commands::kInvalidPropValue, |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 190 | "Invalid value for property '%s'", |
Alex Vakulenko | 5f47206 | 2014-08-14 17:54:04 -0700 | [diff] [blame] | 191 | pair.first.c_str()); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 192 | return false; |
| 193 | } |
| 194 | } |
| 195 | return true; |
| 196 | } |
| 197 | |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 198 | bool TypedValueFromJson(const base::Value* value_in, |
| 199 | const PropType* type, |
| 200 | native_types::Array* value_out, |
| 201 | chromeos::ErrorPtr* error) { |
| 202 | const base::ListValue* list = nullptr; |
| 203 | if (!value_in->GetAsList(&list)) |
| 204 | return ReportUnexpectedJson(value_in, value_out, error); |
| 205 | |
| 206 | CHECK(type) << "Array type definition must be provided"; |
| 207 | CHECK(ValueType::Array == type->GetType()) << "Type must be Array"; |
| 208 | const PropType* item_type = type->GetArray()->GetItemTypePtr(); |
| 209 | CHECK(item_type) << "Incomplete array type definition"; |
| 210 | |
Alex Vakulenko | 3e864c0 | 2015-03-24 10:19:32 -0700 | [diff] [blame] | 211 | // This value might already contain values from the type defaults. |
| 212 | // Clear them first before proceeding. |
| 213 | value_out->clear(); |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 214 | value_out->reserve(list->GetSize()); |
| 215 | for (const base::Value* item : *list) { |
| 216 | std::unique_ptr<PropValue> prop_value = item_type->CreateValue(); |
| 217 | if (!prop_value->FromJson(item, error) || |
| 218 | !item_type->ValidateValue(prop_value->GetValueAsAny(), error)) { |
| 219 | return false; |
| 220 | } |
| 221 | value_out->push_back(std::move(prop_value)); |
| 222 | } |
| 223 | return true; |
| 224 | } |
| 225 | |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 226 | // Compares two sets of key-value pairs from two Objects. |
| 227 | static bool obj_cmp(const native_types::Object::value_type& v1, |
| 228 | const native_types::Object::value_type& v2) { |
| 229 | return (v1.first == v2.first) && v1.second->IsEqual(v2.second.get()); |
| 230 | } |
| 231 | |
| 232 | bool operator==(const native_types::Object& obj1, |
| 233 | const native_types::Object& obj2) { |
| 234 | if (obj1.size() != obj2.size()) |
| 235 | return false; |
| 236 | |
| 237 | auto pair = std::mismatch(obj1.begin(), obj1.end(), obj2.begin(), obj_cmp); |
| 238 | return pair == std::make_pair(obj1.end(), obj2.end()); |
| 239 | } |
| 240 | |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 241 | bool operator==(const native_types::Array& arr1, |
| 242 | const native_types::Array& arr2) { |
| 243 | if (arr1.size() != arr2.size()) |
| 244 | return false; |
| 245 | |
| 246 | using Type = const native_types::Array::value_type; |
| 247 | // Compare two array items. |
| 248 | auto arr_cmp = [](const Type& v1, const Type& v2) { |
| 249 | return v1->IsEqual(v2.get()); |
| 250 | }; |
| 251 | auto pair = std::mismatch(arr1.begin(), arr1.end(), arr2.begin(), arr_cmp); |
| 252 | return pair == std::make_pair(arr1.end(), arr2.end()); |
| 253 | } |
| 254 | |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 255 | std::string ToString(const native_types::Object& obj) { |
| 256 | auto val = TypedValueToJson(obj, nullptr); |
| 257 | std::string str; |
| 258 | base::JSONWriter::Write(val.get(), &str); |
| 259 | return str; |
| 260 | } |
| 261 | |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 262 | std::string ToString(const native_types::Array& arr) { |
| 263 | auto val = TypedValueToJson(arr, nullptr); |
| 264 | std::string str; |
| 265 | base::JSONWriter::Write(val.get(), &str); |
| 266 | return str; |
| 267 | } |
| 268 | |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 269 | chromeos::Any PropValueToDBusVariant(const PropValue* value) { |
Alex Vakulenko | 7245261 | 2015-03-24 09:58:04 -0700 | [diff] [blame] | 270 | if (value->GetType() == ValueType::Object) |
| 271 | return ObjectToDBusVariant(value->GetObject()->GetValue()); |
| 272 | |
| 273 | if (value->GetType() == ValueType::Array) { |
| 274 | const PropType* item_type = |
| 275 | value->GetPropType()->GetArray()->GetItemTypePtr(); |
| 276 | return item_type->ConvertArrayToDBusVariant(value->GetArray()->GetValue()); |
| 277 | } |
| 278 | return value->GetValueAsAny(); |
Anton Muhin | cfde869 | 2014-11-25 03:36:59 +0400 | [diff] [blame] | 279 | } |
| 280 | |
| 281 | chromeos::VariantDictionary |
| 282 | ObjectToDBusVariant(const native_types::Object& object) { |
Alex Vakulenko | 576c979 | 2014-09-22 16:49:45 -0700 | [diff] [blame] | 283 | chromeos::VariantDictionary dict; |
Anton Muhin | cfde869 | 2014-11-25 03:36:59 +0400 | [diff] [blame] | 284 | for (const auto& pair : object) { |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 285 | // Since we are inserting the elements from native_types::Object which is |
| 286 | // a map, the keys are already sorted. So use the "end()" position as a hint |
| 287 | // for dict.insert() so the destination map can optimize its insertion |
| 288 | // time. |
| 289 | chromeos::Any prop = PropValueToDBusVariant(pair.second.get()); |
| 290 | dict.emplace_hint(dict.end(), pair.first, std::move(prop)); |
| 291 | } |
Anton Muhin | cfde869 | 2014-11-25 03:36:59 +0400 | [diff] [blame] | 292 | return dict; |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 293 | } |
| 294 | |
Alex Vakulenko | 5ef7579 | 2015-03-19 15:50:44 -0700 | [diff] [blame] | 295 | std::unique_ptr<const PropValue> PropValueFromDBusVariant( |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 296 | const PropType* type, |
| 297 | const chromeos::Any& value, |
| 298 | chromeos::ErrorPtr* error) { |
Alex Vakulenko | 5ef7579 | 2015-03-19 15:50:44 -0700 | [diff] [blame] | 299 | std::unique_ptr<const PropValue> result; |
Alex Vakulenko | 7245261 | 2015-03-24 09:58:04 -0700 | [diff] [blame] | 300 | if (type->GetType() == ValueType::Array) { |
| 301 | // Special case for array types. |
| 302 | // We expect the |value| to contain std::vector<T>, while PropValue must use |
| 303 | // native_types::Array instead. Do the conversion. |
| 304 | native_types::Array arr; |
| 305 | const PropType* item_type = type->GetArray()->GetItemTypePtr(); |
| 306 | if (item_type->ConvertDBusVariantToArray(value, &arr, error)) |
| 307 | result = type->CreateValue(arr, error); |
| 308 | } else if (type->GetType() == ValueType::Object) { |
Anton Muhin | cfde869 | 2014-11-25 03:36:59 +0400 | [diff] [blame] | 309 | // Special case for object types. |
| 310 | // We expect the |value| to contain chromeos::VariantDictionary, while |
| 311 | // PropValue must use native_types::Object instead. Do the conversion. |
| 312 | if (!value.IsTypeCompatible<chromeos::VariantDictionary>()) { |
| 313 | type->GenerateErrorValueTypeMismatch(error); |
Alex Vakulenko | 7245261 | 2015-03-24 09:58:04 -0700 | [diff] [blame] | 314 | return result; |
Anton Muhin | cfde869 | 2014-11-25 03:36:59 +0400 | [diff] [blame] | 315 | } |
Alex Vakulenko | d94656e | 2015-03-18 09:54:37 -0700 | [diff] [blame] | 316 | CHECK(nullptr != type->GetObject()->GetObjectSchemaPtr()) |
Anton Muhin | cfde869 | 2014-11-25 03:36:59 +0400 | [diff] [blame] | 317 | << "An object type must have a schema defined for it"; |
| 318 | native_types::Object obj; |
Alex Vakulenko | d94656e | 2015-03-18 09:54:37 -0700 | [diff] [blame] | 319 | if (!ObjectFromDBusVariant(type->GetObject()->GetObjectSchemaPtr(), |
Anton Muhin | cfde869 | 2014-11-25 03:36:59 +0400 | [diff] [blame] | 320 | value.Get<chromeos::VariantDictionary>(), |
| 321 | &obj, |
Alex Vakulenko | 7245261 | 2015-03-24 09:58:04 -0700 | [diff] [blame] | 322 | error)) { |
| 323 | return result; |
| 324 | } |
Anton Muhin | cfde869 | 2014-11-25 03:36:59 +0400 | [diff] [blame] | 325 | |
| 326 | result = type->CreateValue(std::move(obj), error); |
| 327 | } else { |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 328 | result = type->CreateValue(value, error); |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 329 | } |
| 330 | |
Anton Muhin | cfde869 | 2014-11-25 03:36:59 +0400 | [diff] [blame] | 331 | return result; |
| 332 | } |
| 333 | |
| 334 | bool ObjectFromDBusVariant(const ObjectSchema* object_schema, |
| 335 | const chromeos::VariantDictionary& dict, |
| 336 | native_types::Object* obj, |
| 337 | chromeos::ErrorPtr* error) { |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 338 | std::set<std::string> keys_processed; |
Alex Vakulenko | 3e864c0 | 2015-03-24 10:19:32 -0700 | [diff] [blame] | 339 | obj->clear(); |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 340 | // First go over all object parameters defined by type's object schema and |
| 341 | // extract the corresponding parameters from the source dictionary. |
Anton Muhin | cfde869 | 2014-11-25 03:36:59 +0400 | [diff] [blame] | 342 | for (const auto& pair : object_schema->GetProps()) { |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 343 | const PropValue* def_value = pair.second->GetDefaultValue(); |
| 344 | auto it = dict.find(pair.first); |
| 345 | if (it != dict.end()) { |
| 346 | const PropType* prop_type = pair.second.get(); |
| 347 | CHECK(prop_type) << "Value property type must be available"; |
| 348 | auto prop_value = PropValueFromDBusVariant(prop_type, it->second, error); |
| 349 | if (!prop_value) { |
Alex Vakulenko | ac8037d | 2014-11-11 11:42:05 -0800 | [diff] [blame] | 350 | chromeos::Error::AddToPrintf(error, FROM_HERE, |
| 351 | errors::commands::kDomain, |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 352 | errors::commands::kInvalidPropValue, |
| 353 | "Invalid value for property '%s'", |
| 354 | pair.first.c_str()); |
Anton Muhin | cfde869 | 2014-11-25 03:36:59 +0400 | [diff] [blame] | 355 | return false; |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 356 | } |
Anton Muhin | cfde869 | 2014-11-25 03:36:59 +0400 | [diff] [blame] | 357 | obj->emplace_hint(obj->end(), pair.first, std::move(prop_value)); |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 358 | } else if (def_value) { |
Alex Vakulenko | 5ef7579 | 2015-03-19 15:50:44 -0700 | [diff] [blame] | 359 | obj->emplace_hint(obj->end(), pair.first, def_value->Clone()); |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 360 | } else { |
| 361 | ErrorMissingProperty(error, pair.first.c_str()); |
Anton Muhin | cfde869 | 2014-11-25 03:36:59 +0400 | [diff] [blame] | 362 | return false; |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 363 | } |
| 364 | keys_processed.insert(pair.first); |
| 365 | } |
| 366 | |
| 367 | // Make sure that we processed all the necessary properties and there weren't |
| 368 | // any extra (unknown) ones specified, unless the schema allows them. |
Anton Muhin | cfde869 | 2014-11-25 03:36:59 +0400 | [diff] [blame] | 369 | if (!object_schema->GetExtraPropertiesAllowed()) { |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 370 | for (const auto& pair : dict) { |
| 371 | if (keys_processed.find(pair.first) == keys_processed.end()) { |
Alex Vakulenko | ac8037d | 2014-11-11 11:42:05 -0800 | [diff] [blame] | 372 | chromeos::Error::AddToPrintf(error, FROM_HERE, |
| 373 | errors::commands::kDomain, |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 374 | errors::commands::kUnknownProperty, |
| 375 | "Unrecognized property '%s'", |
| 376 | pair.first.c_str()); |
Anton Muhin | cfde869 | 2014-11-25 03:36:59 +0400 | [diff] [blame] | 377 | return false; |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 378 | } |
| 379 | } |
| 380 | } |
| 381 | |
Anton Muhin | cfde869 | 2014-11-25 03:36:59 +0400 | [diff] [blame] | 382 | return true; |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 383 | } |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 384 | |
| 385 | } // namespace buffet |