blob: 1191264ae2954e34de03f9981b879e81cab20b4c [file] [log] [blame]
// Copyright 2014 The Chromium OS 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 "src/commands/schema_utils.h"
#include <algorithm>
#include <set>
#include <string>
#include <base/json/json_writer.h>
#include <base/logging.h>
#include "src/commands/object_schema.h"
#include "src/commands/prop_types.h"
#include "src/commands/prop_values.h"
namespace weave {
namespace {
// Helper function to report "type mismatch" errors when parsing JSON.
void ReportJsonTypeMismatch(const tracked_objects::Location& location,
const base::Value* value_in,
const std::string& expected_type,
ErrorPtr* error) {
std::string value_as_string;
base::JSONWriter::Write(*value_in, &value_as_string);
Error::AddToPrintf(error, location, errors::commands::kDomain,
errors::commands::kTypeMismatch,
"Unable to convert value %s into %s",
value_as_string.c_str(), expected_type.c_str());
}
// Template version of ReportJsonTypeMismatch that deduces the type of expected
// data from the value_out parameter passed to particular overload of
// TypedValueFromJson() function. Always returns false.
template <typename T>
bool ReportUnexpectedJson(const tracked_objects::Location& location,
const base::Value* value_in,
T*,
ErrorPtr* error) {
ReportJsonTypeMismatch(location, value_in,
PropType::GetTypeStringFromType(GetValueType<T>()),
error);
return false;
}
bool ErrorMissingProperty(ErrorPtr* error,
const tracked_objects::Location& location,
const char* param_name) {
Error::AddToPrintf(error, location, errors::commands::kDomain,
errors::commands::kPropertyMissing,
"Required parameter missing: %s", param_name);
return false;
}
} // namespace
// Specializations of TypedValueToJson<T>() for supported C++ types.
std::unique_ptr<base::FundamentalValue> TypedValueToJson(bool value) {
return std::unique_ptr<base::FundamentalValue>(
new base::FundamentalValue(value));
}
std::unique_ptr<base::FundamentalValue> TypedValueToJson(int value) {
return std::unique_ptr<base::FundamentalValue>(
new base::FundamentalValue(value));
}
std::unique_ptr<base::FundamentalValue> TypedValueToJson(double value) {
return std::unique_ptr<base::FundamentalValue>(
new base::FundamentalValue(value));
}
std::unique_ptr<base::StringValue> TypedValueToJson(const std::string& value) {
return std::unique_ptr<base::StringValue>(new base::StringValue(value));
}
std::unique_ptr<base::DictionaryValue> TypedValueToJson(const ValueMap& value) {
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
for (const auto& pair : value) {
auto prop_value = pair.second->ToJson();
CHECK(prop_value);
dict->SetWithoutPathExpansion(pair.first, prop_value.release());
}
return dict;
}
std::unique_ptr<base::ListValue> TypedValueToJson(const ValueVector& value) {
std::unique_ptr<base::ListValue> list(new base::ListValue);
for (const auto& item : value) {
auto json = item->ToJson();
CHECK(json);
list->Append(json.release());
}
return list;
}
bool TypedValueFromJson(const base::Value* value_in,
const PropType* type,
bool* value_out,
ErrorPtr* error) {
return value_in->GetAsBoolean(value_out) ||
ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
}
bool TypedValueFromJson(const base::Value* value_in,
const PropType* type,
int* value_out,
ErrorPtr* error) {
return value_in->GetAsInteger(value_out) ||
ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
}
bool TypedValueFromJson(const base::Value* value_in,
const PropType* type,
double* value_out,
ErrorPtr* error) {
return value_in->GetAsDouble(value_out) ||
ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
}
bool TypedValueFromJson(const base::Value* value_in,
const PropType* type,
std::string* value_out,
ErrorPtr* error) {
return value_in->GetAsString(value_out) ||
ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
}
bool TypedValueFromJson(const base::Value* value_in,
const PropType* type,
ValueMap* value_out,
ErrorPtr* error) {
const base::DictionaryValue* dict = nullptr;
if (!value_in->GetAsDictionary(&dict))
return ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
CHECK(type) << "Object definition must be provided";
CHECK(ValueType::Object == type->GetType()) << "Type must be Object";
const ObjectSchema* object_schema = type->GetObject()->GetObjectSchemaPtr();
std::set<std::string> keys_processed;
value_out->clear(); // Clear possible default values already in |value_out|.
for (const auto& pair : object_schema->GetProps()) {
const PropValue* def_value = pair.second->GetDefaultValue();
if (dict->HasKey(pair.first)) {
const base::Value* param_value = nullptr;
CHECK(dict->GetWithoutPathExpansion(pair.first, &param_value))
<< "Unable to get parameter";
auto value = pair.second->CreatePropValue(*param_value, error);
if (!value) {
Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
errors::commands::kInvalidPropValue,
"Invalid value for property '%s'",
pair.first.c_str());
return false;
}
value_out->emplace_hint(value_out->end(), pair.first, std::move(value));
keys_processed.insert(pair.first);
} else if (def_value) {
value_out->emplace_hint(value_out->end(), pair.first, def_value->Clone());
keys_processed.insert(pair.first);
} else if (pair.second->IsRequired()) {
return ErrorMissingProperty(error, FROM_HERE, pair.first.c_str());
}
}
// Just for sanity, make sure that we processed all the necessary properties
// and there weren't any extra (unknown) ones specified. If so, ignore
// them, but log as warnings...
base::DictionaryValue::Iterator iter(*dict);
while (!iter.IsAtEnd()) {
std::string key = iter.key();
if (keys_processed.find(key) == keys_processed.end() &&
!object_schema->GetExtraPropertiesAllowed()) {
Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
errors::commands::kUnknownProperty,
"Unrecognized parameter '%s'", key.c_str());
return false;
}
iter.Advance();
}
// Now go over all property values and validate them.
for (const auto& pair : *value_out) {
const PropType* prop_type = pair.second->GetPropType();
CHECK(prop_type) << "Value property type must be available";
if (!prop_type->ValidateConstraints(*pair.second, error)) {
Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
errors::commands::kInvalidPropValue,
"Invalid value for property '%s'", pair.first.c_str());
return false;
}
}
return true;
}
bool TypedValueFromJson(const base::Value* value_in,
const PropType* type,
ValueVector* value_out,
ErrorPtr* error) {
const base::ListValue* list = nullptr;
if (!value_in->GetAsList(&list))
return ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
CHECK(type) << "Array type definition must be provided";
CHECK(ValueType::Array == type->GetType()) << "Type must be Array";
const PropType* item_type = type->GetArray()->GetItemTypePtr();
CHECK(item_type) << "Incomplete array type definition";
// This value might already contain values from the type defaults.
// Clear them first before proceeding.
value_out->clear();
value_out->reserve(list->GetSize());
for (const base::Value* item : *list) {
std::unique_ptr<PropValue> prop_value =
item_type->CreatePropValue(*item, error);
if (!prop_value)
return false;
value_out->push_back(std::move(prop_value));
}
return true;
}
// Compares two sets of key-value pairs from two Objects.
static bool obj_cmp(const ValueMap::value_type& v1,
const ValueMap::value_type& v2) {
return (v1.first == v2.first) && v1.second->IsEqual(v2.second.get());
}
bool operator==(const ValueMap& obj1, const ValueMap& obj2) {
if (obj1.size() != obj2.size())
return false;
auto pair = std::mismatch(obj1.begin(), obj1.end(), obj2.begin(), obj_cmp);
return pair == std::make_pair(obj1.end(), obj2.end());
}
bool operator==(const ValueVector& arr1, const ValueVector& arr2) {
if (arr1.size() != arr2.size())
return false;
using Type = const ValueVector::value_type;
// Compare two array items.
auto arr_cmp = [](const Type& v1, const Type& v2) {
return v1->IsEqual(v2.get());
};
auto pair = std::mismatch(arr1.begin(), arr1.end(), arr2.begin(), arr_cmp);
return pair == std::make_pair(arr1.end(), arr2.end());
}
std::string ToString(const ValueMap& obj) {
auto val = TypedValueToJson(obj);
std::string str;
base::JSONWriter::Write(*val, &str);
return str;
}
std::string ToString(const ValueVector& arr) {
auto val = TypedValueToJson(arr);
std::string str;
base::JSONWriter::Write(*val, &str);
return str;
}
} // namespace weave