libweave: Cleanup creation of PropValues

Hide some constructors and test only methods.

BUG=brillo:1246
TEST='FEATURES=test emerge-gizmo buffet'

Change-Id: I1343809f94999795f8f162e3464ac22413b0dbd5
Reviewed-on: https://chromium-review.googlesource.com/289645
Reviewed-by: Vitaly Buka <vitalybuka@chromium.org>
Commit-Queue: Vitaly Buka <vitalybuka@chromium.org>
Tested-by: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/libweave/src/commands/object_schema_unittest.cc b/libweave/src/commands/object_schema_unittest.cc
index 018c28f..fc04dd5 100644
--- a/libweave/src/commands/object_schema_unittest.cc
+++ b/libweave/src/commands/object_schema_unittest.cc
@@ -47,6 +47,13 @@
   return GetArrayValues<T>(one_of->set_.value);
 }
 
+bool ValidateValue(const PropType& type,
+                   const base::Value& value,
+                   chromeos::ErrorPtr* error) {
+  std::unique_ptr<PropValue> val = type.CreatePropValue(value, error);
+  return val != nullptr;
+}
+
 }  // anonymous namespace
 
 TEST(CommandSchema, IntPropType_Empty) {
@@ -123,31 +130,31 @@
   IntPropType prop;
   prop.AddMinMaxConstraint(2, 4);
   chromeos::ErrorPtr error;
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("-1").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("-1"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("0").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("0"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("1").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("1"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
-  EXPECT_TRUE(prop.ValidateValue(CreateValue("2").get(), &error));
+  EXPECT_TRUE(ValidateValue(prop, *CreateValue("2"), &error));
   EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(prop.ValidateValue(CreateValue("3").get(), &error));
+  EXPECT_TRUE(ValidateValue(prop, *CreateValue("3"), &error));
   EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(prop.ValidateValue(CreateValue("4").get(), &error));
+  EXPECT_TRUE(ValidateValue(prop, *CreateValue("4"), &error));
   EXPECT_EQ(nullptr, error.get());
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("5").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("5"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("true").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("true"), &error));
   EXPECT_EQ("type_mismatch", error->GetCode());
   error.reset();
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("3.0").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("3.0"), &error));
   EXPECT_EQ("type_mismatch", error->GetCode());
   error.reset();
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("'3'").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'3'"), &error));
   EXPECT_EQ("type_mismatch", error->GetCode());
 }
 
@@ -225,18 +232,18 @@
   BooleanPropType prop;
   prop.FromJson(CreateDictionaryValue("{'enum':[true]}").get(), &prop, nullptr);
   chromeos::ErrorPtr error;
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("false").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("false"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
-  EXPECT_TRUE(prop.ValidateValue(CreateValue("true").get(), &error));
+  EXPECT_TRUE(ValidateValue(prop, *CreateValue("true"), &error));
   error.reset();
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("1").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("1"), &error));
   EXPECT_EQ("type_mismatch", error->GetCode());
   error.reset();
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("3.0").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("3.0"), &error));
   EXPECT_EQ("type_mismatch", error->GetCode());
   error.reset();
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("'3'").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'3'"), &error));
   EXPECT_EQ("type_mismatch", error->GetCode());
 }
 
@@ -335,25 +342,25 @@
   DoublePropType prop;
   prop.AddMinMaxConstraint(-1.2, 1.3);
   chromeos::ErrorPtr error;
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("-2").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("-2"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("-1.3").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("-1.3"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
-  EXPECT_TRUE(prop.ValidateValue(CreateValue("-1.2").get(), &error));
+  EXPECT_TRUE(ValidateValue(prop, *CreateValue("-1.2"), &error));
   EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(prop.ValidateValue(CreateValue("0.0").get(), &error));
+  EXPECT_TRUE(ValidateValue(prop, *CreateValue("0.0"), &error));
   EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(prop.ValidateValue(CreateValue("1.3").get(), &error));
+  EXPECT_TRUE(ValidateValue(prop, *CreateValue("1.3"), &error));
   EXPECT_EQ(nullptr, error.get());
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("1.31").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("1.31"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("true").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("true"), &error));
   EXPECT_EQ("type_mismatch", error->GetCode());
   error.reset();
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("'0.0'").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'0.0'"), &error));
   EXPECT_EQ("type_mismatch", error->GetCode());
 }
 
@@ -454,30 +461,30 @@
   StringPropType prop;
   prop.AddLengthConstraint(1, 3);
   chromeos::ErrorPtr error;
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("''").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("''"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
   prop.AddLengthConstraint(2, 3);
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("''").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("''"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("'a'").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'a'"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
-  EXPECT_TRUE(prop.ValidateValue(CreateValue("'ab'").get(), &error));
+  EXPECT_TRUE(ValidateValue(prop, *CreateValue("'ab'"), &error));
   EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(prop.ValidateValue(CreateValue("'abc'").get(), &error));
+  EXPECT_TRUE(ValidateValue(prop, *CreateValue("'abc'"), &error));
   EXPECT_EQ(nullptr, error.get());
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("'abcd'").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'abcd'"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
 
   prop.FromJson(CreateDictionaryValue("{'enum':['abc','def','xyz!!']}").get(),
                 nullptr, &error);
-  EXPECT_TRUE(prop.ValidateValue(CreateValue("'abc'").get(), &error));
-  EXPECT_TRUE(prop.ValidateValue(CreateValue("'def'").get(), &error));
-  EXPECT_TRUE(prop.ValidateValue(CreateValue("'xyz!!'").get(), &error));
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("'xyz'").get(), &error));
+  EXPECT_TRUE(ValidateValue(prop, *CreateValue("'abc'"), &error));
+  EXPECT_TRUE(ValidateValue(prop, *CreateValue("'def'"), &error));
+  EXPECT_TRUE(ValidateValue(prop, *CreateValue("'xyz!!'"), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'xyz'"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
 }
@@ -676,30 +683,30 @@
                     "'required':['expires','password']}").get(),
                 nullptr, nullptr);
   chromeos::ErrorPtr error;
-  EXPECT_TRUE(prop.ValidateValue(
-      CreateValue("{'expires':10,'password':'abcdef'}").get(), &error));
+  EXPECT_TRUE(ValidateValue(
+      prop, *CreateValue("{'expires':10,'password':'abcdef'}"), &error));
   error.reset();
 
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("{'expires':10}").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("{'expires':10}"), &error));
   EXPECT_EQ("parameter_missing", error->GetCode());
   error.reset();
 
   EXPECT_FALSE(
-      prop.ValidateValue(CreateValue("{'password':'abcdef'}").get(), &error));
+      ValidateValue(prop, *CreateValue("{'password':'abcdef'}"), &error));
   EXPECT_EQ("parameter_missing", error->GetCode());
   error.reset();
 
-  EXPECT_FALSE(prop.ValidateValue(
-      CreateValue("{'expires':10,'password':'abcde'}").get(), &error));
+  EXPECT_FALSE(ValidateValue(
+      prop, *CreateValue("{'expires':10,'password':'abcde'}"), &error));
   EXPECT_EQ("out_of_range", error->GetFirstError()->GetCode());
   error.reset();
 
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("2").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("2"), &error));
   EXPECT_EQ("type_mismatch", error->GetCode());
   error.reset();
 
-  EXPECT_FALSE(prop.ValidateValue(
-      CreateValue("{'expires':10,'password':'abcdef','retry':true}").get(),
+  EXPECT_FALSE(ValidateValue(
+      prop, *CreateValue("{'expires':10,'password':'abcdef','retry':true}"),
       &error));
   EXPECT_EQ("unexpected_parameter", error->GetCode());
   error.reset();
@@ -714,16 +721,16 @@
           .get(),
       nullptr, nullptr));
   chromeos::ErrorPtr error;
-  EXPECT_TRUE(prop.ValidateValue(CreateValue("{'height':20,'width':10}").get(),
-                                 &error));
+  EXPECT_TRUE(
+      ValidateValue(prop, *CreateValue("{'height':20,'width':10}"), &error));
   error.reset();
 
-  EXPECT_TRUE(prop.ValidateValue(
-      CreateValue("{'height':200,'width':100}").get(), &error));
+  EXPECT_TRUE(
+      ValidateValue(prop, *CreateValue("{'height':200,'width':100}"), &error));
   error.reset();
 
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("{'height':12,'width':10}").get(),
-                                  &error));
+  EXPECT_FALSE(
+      ValidateValue(prop, *CreateValue("{'height':12,'width':10}"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
 }
@@ -825,14 +832,14 @@
       nullptr, nullptr);
 
   chromeos::ErrorPtr error;
-  EXPECT_TRUE(prop.ValidateValue(CreateValue("[3,4,10.5]").get(), &error));
+  EXPECT_TRUE(ValidateValue(prop, *CreateValue("[3,4,10.5]"), &error));
   error.reset();
 
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("[2]").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("[2]"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
 
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("[4, 5, 20]").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("[4, 5, 20]"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
 }
@@ -845,14 +852,14 @@
       nullptr, nullptr);
 
   chromeos::ErrorPtr error;
-  EXPECT_TRUE(prop.ValidateValue(CreateValue("[2,3]").get(), &error));
+  EXPECT_TRUE(ValidateValue(prop, *CreateValue("[2,3]"), &error));
   error.reset();
 
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("[2]").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("[2]"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
 
-  EXPECT_FALSE(prop.ValidateValue(CreateValue("[2,3,4]").get(), &error));
+  EXPECT_FALSE(ValidateValue(prop, *CreateValue("[2,3,4]"), &error));
   EXPECT_EQ("out_of_range", error->GetCode());
   error.reset();
 }
@@ -1267,8 +1274,9 @@
       prop.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr));
 
   // Omit all.
-  auto value = prop.CreateValue();
-  ASSERT_TRUE(value->FromJson(CreateDictionaryValue("{}").get(), nullptr));
+  auto value =
+      prop.CreatePropValue(*CreateDictionaryValue("{}").get(), nullptr);
+  ASSERT_NE(nullptr, value);
   ValueMap obj = value->GetObject()->GetValue();
   EXPECT_TRUE(obj["param1"]->GetBoolean()->GetValue());
   EXPECT_EQ(2, obj["param2"]->GetInt()->GetValue());
@@ -1281,14 +1289,14 @@
   EXPECT_EQ((std::vector<int>{1, 2, 3}), GetArrayValues<int>(param6));
 
   // Specify some.
-  value = prop.CreateValue();
   const char* val_json =
       "{"
       "'param1':false,"
       "'param3':33.3,"
       "'param5':{'x':-5,'y':-6}"
       "}";
-  ASSERT_TRUE(value->FromJson(CreateDictionaryValue(val_json).get(), nullptr));
+  value = prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr);
+  ASSERT_NE(nullptr, value);
   obj = value->GetObject()->GetValue();
   EXPECT_FALSE(obj["param1"]->GetBoolean()->GetValue());
   EXPECT_EQ(2, obj["param2"]->GetInt()->GetValue());
@@ -1301,7 +1309,6 @@
   EXPECT_EQ((std::vector<int>{1, 2, 3}), GetArrayValues<int>(param6));
 
   // Specify all.
-  value = prop.CreateValue();
   val_json =
       "{"
       "'param1':false,"
@@ -1311,7 +1318,8 @@
       "'param5':{'x':-55,'y':66},"
       "'param6':[-1, 0]"
       "}";
-  ASSERT_TRUE(value->FromJson(CreateDictionaryValue(val_json).get(), nullptr));
+  value = prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr);
+  ASSERT_NE(nullptr, value);
   obj = value->GetObject()->GetValue();
   EXPECT_FALSE(obj["param1"]->GetBoolean()->GetValue());
   EXPECT_EQ(22, obj["param2"]->GetInt()->GetValue());
@@ -1638,23 +1646,24 @@
   ASSERT_TRUE(prop.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
                             nullptr));
 
-  auto value = prop.CreateValue();
   auto val_json = R"({
     'param1':10,
     'param2':20,
     'param3':30,
     'param4':40
   })";
-  ASSERT_TRUE(value->FromJson(CreateDictionaryValue(val_json).get(), nullptr));
+  auto value =
+      prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr);
+  ASSERT_NE(nullptr, value);
   ValueMap obj = value->GetObject()->GetValue();
   EXPECT_EQ(10, obj["param1"]->GetInt()->GetValue());
   EXPECT_EQ(20, obj["param2"]->GetInt()->GetValue());
   EXPECT_EQ(30, obj["param3"]->GetInt()->GetValue());
   EXPECT_EQ(40, obj["param4"]->GetInt()->GetValue());
 
-  value = prop.CreateValue();
   val_json = "{'param1':100}";
-  ASSERT_TRUE(value->FromJson(CreateDictionaryValue(val_json).get(), nullptr));
+  value = prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr);
+  ASSERT_NE(nullptr, value);
   obj = value->GetObject()->GetValue();
   EXPECT_EQ(3, obj.size());
 
@@ -1678,10 +1687,11 @@
   ASSERT_TRUE(prop.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
                             nullptr));
 
-  auto value = prop.CreateValue();
   auto val_json = "{'param2':20}";
   chromeos::ErrorPtr error;
-  ASSERT_FALSE(value->FromJson(CreateDictionaryValue(val_json).get(), &error));
+  auto value =
+      prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), &error);
+  ASSERT_EQ(nullptr, value);
   EXPECT_EQ(errors::commands::kPropertyMissing, error->GetCode());
 }
 
diff --git a/libweave/src/commands/prop_types.cc b/libweave/src/commands/prop_types.cc
index 4ddfc00..6daabc0 100644
--- a/libweave/src/commands/prop_types.cc
+++ b/libweave/src/commands/prop_types.cc
@@ -185,8 +185,8 @@
   // so we can parse and validate the value of the default.
   const base::Value* defval = nullptr;  // Owned by value
   if (value->GetWithoutPathExpansion(commands::attributes::kDefault, &defval)) {
-    std::unique_ptr<PropValue> prop_value = CreateValue();
-    if (!prop_value->FromJson(defval, error)) {
+    std::unique_ptr<PropValue> prop_value = CreatePropValue(*defval, error);
+    if (!prop_value) {
       chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
                                    errors::commands::kInvalidPropValue,
                                    "Invalid value for property '%s'",
@@ -231,13 +231,6 @@
   return p != constraints_.end() ? p->second.get() : nullptr;
 }
 
-bool PropType::ValidateValue(const base::Value* value,
-                             chromeos::ErrorPtr* error) const {
-  std::unique_ptr<PropValue> val = CreateValue();
-  CHECK(val) << "Failed to create value object";
-  return val->FromJson(value, error);
-}
-
 bool PropType::ValidateConstraints(const PropValue& value,
                                    chromeos::ErrorPtr* error) const {
   for (const auto& pair : constraints_) {
diff --git a/libweave/src/commands/prop_types.h b/libweave/src/commands/prop_types.h
index 71de94d..c8b1338 100644
--- a/libweave/src/commands/prop_types.h
+++ b/libweave/src/commands/prop_types.h
@@ -85,9 +85,10 @@
   // Makes a full copy of this type definition.
   virtual std::unique_ptr<PropType> Clone() const;
 
-  // Creates an instance of associated value object, using the parameter
-  // type as a factory class.
-  virtual std::unique_ptr<PropValue> CreateValue() const = 0;
+  // Creates an instance of associated value object.
+  virtual std::unique_ptr<PropValue> CreatePropValue(
+      const base::Value& value,
+      chromeos::ErrorPtr* error) const = 0;
 
   // Saves the parameter type definition as a JSON object.
   // If |full_schema| is set to true, the full type definition is saved,
@@ -125,13 +126,6 @@
     return true;
   }
 
-  // Validates a JSON value for the parameter type to make sure it satisfies
-  // the parameter type definition including any specified constraints.
-  // Returns false if the |value| does not meet the requirements of the type
-  // definition and returns additional information about the failure via
-  // the |error| parameter.
-  bool ValidateValue(const base::Value* value, chromeos::ErrorPtr* error) const;
-
   // Additional helper static methods to help with converting a type enum
   // value into a string and back.
   using TypeMap = std::vector<std::pair<ValueType, std::string>>;
@@ -162,6 +156,9 @@
                            chromeos::ErrorPtr* error) const;
 
  protected:
+  friend class StatePackage;
+  virtual std::unique_ptr<PropValue> CreateDefaultValue() const = 0;
+
   // Specifies if this parameter definition is derived from a base
   // object schema.
   bool based_on_schema_ = false;
@@ -188,23 +185,27 @@
   // Overrides from PropType.
   ValueType GetType() const override { return GetValueType<T>(); }
 
-  std::unique_ptr<PropValue> CreateValue() const override {
-    if (GetDefaultValue())
-      return GetDefaultValue()->Clone();
-    return std::unique_ptr<PropValue>{new Value{Clone()}};
+  std::unique_ptr<PropValue> CreatePropValue(
+      const base::Value& value,
+      chromeos::ErrorPtr* error) const override {
+    return CreateValue(value, error);
   }
 
   std::unique_ptr<Value> CreateValue(const base::Value& value,
                                      chromeos::ErrorPtr* error) const {
-    std::unique_ptr<Value> prop_value{new Value{Clone()}};
-    if (!prop_value->FromJson(&value, error))
-      return nullptr;
-    return prop_value;
+    return Value::CreateFromJson(value, *this, error);
   }
 
   bool ConstraintsFromJson(const base::DictionaryValue* value,
                            std::set<std::string>* processed_keys,
                            chromeos::ErrorPtr* error) override;
+
+ protected:
+  std::unique_ptr<PropValue> CreateDefaultValue() const override {
+    if (GetDefaultValue())
+      return GetDefaultValue()->Clone();
+    return std::unique_ptr<PropValue>{new Value{*this}};
+  }
 };
 
 // Helper base class for Int and Double parameter types.
diff --git a/libweave/src/commands/prop_values.cc b/libweave/src/commands/prop_values.cc
index 813f9c6..cc70948 100644
--- a/libweave/src/commands/prop_values.cc
+++ b/libweave/src/commands/prop_values.cc
@@ -8,12 +8,9 @@
 
 namespace weave {
 
-PropValue::PropValue(std::unique_ptr<const PropType> type)
-    : type_{std::move(type)} {
-}
+PropValue::PropValue(const PropType& type) : type_{type.Clone()} {}
 
-PropValue::PropValue(const PropValue& other)
-    : PropValue{other.type_->Clone()} {}
+PropValue::PropValue(const PropValue& other) : PropValue{*other.type_} {}
 
 PropValue::~PropValue() {
 }
diff --git a/libweave/src/commands/prop_values.h b/libweave/src/commands/prop_values.h
index d50c1a4..939356a 100644
--- a/libweave/src/commands/prop_values.h
+++ b/libweave/src/commands/prop_values.h
@@ -83,7 +83,8 @@
 //     This is used to validate the values against "enum"/"one of" constraints.
 class PropValue {
  public:
-  explicit PropValue(std::unique_ptr<const PropType> type);
+  // Only CreateDefaultValue should use this constructor.
+  explicit PropValue(const PropType& type);
   virtual ~PropValue();
 
   // Gets the type of the value.
@@ -108,11 +109,6 @@
 
   // Saves the value as a JSON object. Never fails.
   virtual std::unique_ptr<base::Value> ToJson() const = 0;
-  // Parses a value from JSON.
-  // If it fails, it returns false and provides additional information
-  // via the |error| parameter.
-  virtual bool FromJson(const base::Value* value,
-                        chromeos::ErrorPtr* error) = 0;
 
   // Return the type definition of this value.
   const PropType* GetPropType() const { return type_.get(); }
@@ -134,9 +130,6 @@
 template <typename T>
 class TypedValueBase : public PropValue {
  public:
-  // To help refer to this base class from derived classes, define Base to
-  // be this class.
-  using Base = TypedValueBase<T>;
   using PropValue::PropValue;
 
   // Overrides from PropValue base class.
@@ -146,34 +139,23 @@
     return TypedValueToJson(value_);
   }
 
-  bool FromJson(const base::Value* value, chromeos::ErrorPtr* error) override {
-    T tmp_value;
-    if (!TypedValueFromJson(value, GetPropType(), &tmp_value, error))
-      return false;
-    return SetValue(tmp_value, error);
-  }
-
   bool IsEqual(const PropValue* value) const override {
     if (GetType() != value->GetType())
       return false;
-    const Base* value_base = static_cast<const Base*>(value);
-    return CompareValue(GetValue(), value_base->GetValue());
+    return CompareValue(GetValue(),
+                        static_cast<const TypedValueBase*>(value)->GetValue());
   }
 
   // Helper methods to get and set the C++ representation of the value.
   const T& GetValue() const { return value_; }
-  bool SetValue(T value, chromeos::ErrorPtr* error) {
-    std::swap(value_, value);  // Backup.
-    if (GetPropType()->ValidateConstraints(*this, error))
-      return true;
-    std::swap(value_, value);  // Restore.
-    return false;
-  }
 
  protected:
   explicit TypedValueBase(const TypedValueBase& other)
       : PropValue(other), value_(other.value_) {}
 
+  TypedValueBase(const PropType& type, T value)
+      : PropValue(type), value_(value) {}
+
  private:
   T value_{};  // The value of the parameter in C++ data representation.
 };
@@ -192,12 +174,28 @@
     return std::unique_ptr<PropValue>{
         new Derived{*static_cast<const Derived*>(this)}};
   }
+
+  static std::unique_ptr<Derived> CreateFromJson(const base::Value& value,
+                                                 const PropType& type,
+                                                 chromeos::ErrorPtr* error) {
+    T tmp_value;
+    if (!TypedValueFromJson(&value, &type, &tmp_value, error))
+      return nullptr;
+
+    // Only place where invalid value can exist.
+    std::unique_ptr<Derived> result{new Derived{type, tmp_value}};
+    if (!result->GetPropType()->ValidateConstraints(*result, error))
+      return nullptr;
+
+    return result;
+  }
 };
 
 // Value of type Integer.
 class IntValue final : public TypedValueWithClone<IntValue, int> {
  public:
-  using Base::Base;  // Expose the custom constructor of the base class.
+  using Base::Base;
+  friend class TypedValueWithClone<IntValue, int>;
   IntValue* GetInt() override { return this; }
   IntValue const* GetInt() const override { return this; }
 };
@@ -205,7 +203,8 @@
 // Value of type Number.
 class DoubleValue final : public TypedValueWithClone<DoubleValue, double> {
  public:
-  using Base::Base;  // Expose the custom constructor of the base class.
+  using Base::Base;
+  friend class TypedValueWithClone<DoubleValue, double>;
   DoubleValue* GetDouble() override { return this; }
   DoubleValue const* GetDouble() const override { return this; }
 };
@@ -213,7 +212,8 @@
 // Value of type String.
 class StringValue final : public TypedValueWithClone<StringValue, std::string> {
  public:
-  using Base::Base;  // Expose the custom constructor of the base class.
+  using Base::Base;
+  friend class TypedValueWithClone<StringValue, std::string>;
   StringValue* GetString() override { return this; }
   StringValue const* GetString() const override { return this; }
 };
@@ -221,7 +221,8 @@
 // Value of type Boolean.
 class BooleanValue final : public TypedValueWithClone<BooleanValue, bool> {
  public:
-  using Base::Base;  // Expose the custom constructor of the base class.
+  using Base::Base;
+  friend class TypedValueWithClone<BooleanValue, bool>;
   BooleanValue* GetBoolean() override { return this; }
   BooleanValue const* GetBoolean() const override { return this; }
 };
@@ -229,7 +230,8 @@
 // Value of type Object.
 class ObjectValue final : public TypedValueWithClone<ObjectValue, ValueMap> {
  public:
-  using Base::Base;  // Expose the custom constructor of the base class.
+  using Base::Base;
+  friend class TypedValueWithClone<ObjectValue, ValueMap>;
   ObjectValue* GetObject() override { return this; }
   ObjectValue const* GetObject() const override { return this; }
 };
@@ -237,7 +239,8 @@
 // Value of type Array.
 class ArrayValue final : public TypedValueWithClone<ArrayValue, ValueVector> {
  public:
-  using Base::Base;  // Expose the custom constructor of the base class.
+  using Base::Base;
+  friend class TypedValueWithClone<ArrayValue, ValueVector>;
   ArrayValue* GetArray() override { return this; }
   ArrayValue const* GetArray() const override { return this; }
 };
diff --git a/libweave/src/commands/schema_utils.cc b/libweave/src/commands/schema_utils.cc
index 034eca9..e2c8293 100644
--- a/libweave/src/commands/schema_utils.cc
+++ b/libweave/src/commands/schema_utils.cc
@@ -143,11 +143,11 @@
   for (const auto& pair : object_schema->GetProps()) {
     const PropValue* def_value = pair.second->GetDefaultValue();
     if (dict->HasKey(pair.first)) {
-      auto value = pair.second->CreateValue();
       const base::Value* param_value = nullptr;
       CHECK(dict->GetWithoutPathExpansion(pair.first, &param_value))
           << "Unable to get parameter";
-      if (!value->FromJson(param_value, error)) {
+      auto value = pair.second->CreatePropValue(*param_value, error);
+      if (!value) {
         chromeos::Error::AddToPrintf(
             error, FROM_HERE, errors::commands::kDomain,
             errors::commands::kInvalidPropValue,
@@ -213,10 +213,10 @@
   value_out->clear();
   value_out->reserve(list->GetSize());
   for (const base::Value* item : *list) {
-    std::unique_ptr<PropValue> prop_value = item_type->CreateValue();
-    if (!prop_value->FromJson(item, error)) {
+    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;
diff --git a/libweave/src/commands/unittest_utils.h b/libweave/src/commands/unittest_utils.h
index d0d39ee..043bace 100644
--- a/libweave/src/commands/unittest_utils.h
+++ b/libweave/src/commands/unittest_utils.h
@@ -30,30 +30,27 @@
   return val1.Equals(&val2);
 }
 
-template <typename PropVal, typename T>
-std::unique_ptr<const PropVal> make_prop_value(const T& value) {
-  std::unique_ptr<PropVal> result{
-      new PropVal{PropType::Create(GetValueType<T>())}};
-  if (!result->SetValue(value, nullptr))
-    return nullptr;
-  return std::move(result);
+template <typename T>
+std::unique_ptr<const PropValue> make_prop_value(const base::Value& value) {
+  auto prop_type = PropType::Create(GetValueType<T>());
+  return prop_type->CreatePropValue(value, nullptr);
 }
 
-inline std::unique_ptr<const IntValue> make_int_prop_value(int value) {
-  return make_prop_value<IntValue, int>(value);
+inline std::unique_ptr<const PropValue> make_int_prop_value(int value) {
+  return make_prop_value<int>(base::FundamentalValue{value});
 }
 
-inline std::unique_ptr<const DoubleValue> make_double_prop_value(double value) {
-  return make_prop_value<DoubleValue, double>(value);
+inline std::unique_ptr<const PropValue> make_double_prop_value(double value) {
+  return make_prop_value<double>(base::FundamentalValue{value});
 }
 
-inline std::unique_ptr<const BooleanValue> make_bool_prop_value(bool value) {
-  return make_prop_value<BooleanValue, bool>(value);
+inline std::unique_ptr<const PropValue> make_bool_prop_value(bool value) {
+  return make_prop_value<bool>(base::FundamentalValue{value});
 }
 
-inline std::unique_ptr<const StringValue> make_string_prop_value(
+inline std::unique_ptr<const PropValue> make_string_prop_value(
     const std::string& value) {
-  return make_prop_value<StringValue, std::string>(value);
+  return make_prop_value<std::string>(base::StringValue{value});
 }
 
 }  // namespace unittests
diff --git a/libweave/src/states/state_package.cc b/libweave/src/states/state_package.cc
index 170b03c..e20d267 100644
--- a/libweave/src/states/state_package.cc
+++ b/libweave/src/states/state_package.cc
@@ -38,7 +38,7 @@
   for (const auto& pair : schema.GetProps()) {
     types_.AddProp(pair.first, pair.second->Clone());
     // Create default value for this state property.
-    values_.emplace(pair.first, pair.second->CreateValue());
+    values_.emplace(pair.first, pair.second->CreateDefaultValue());
   }
 
   return true;
@@ -89,8 +89,8 @@
                                  name_.c_str(), property_name.c_str());
     return false;
   }
-  auto new_value = it->second->GetPropType()->CreateValue();
-  if (!new_value->FromJson(&value, error))
+  auto new_value = it->second->GetPropType()->CreatePropValue(value, error);
+  if (!new_value)
     return false;
   it->second = std::move(new_value);
   return true;
diff --git a/libweave/src/states/state_package_unittest.cc b/libweave/src/states/state_package_unittest.cc
index 0bd4be8..d6978c0 100644
--- a/libweave/src/states/state_package_unittest.cc
+++ b/libweave/src/states/state_package_unittest.cc
@@ -330,10 +330,17 @@
   EXPECT_EQ(errors::commands::kUnknownProperty, error->GetCode());
 }
 
+TEST_F(StatePackageTest, SetPropertyValue_Object_OptionalProperty) {
+  EXPECT_JSON_EQ("{'altitude': 89.9, 'azimuth': 57.2957795}",
+                 *package_->GetProperty("direction")->ToJson());
+  ASSERT_TRUE(package_->SetPropertyValue(
+      "direction", *CreateDictionaryValue("{'azimuth': 10.0}"), nullptr));
+  EXPECT_JSON_EQ("{'azimuth': 10.0}",
+                 *package_->GetProperty("direction")->ToJson());
+}
+
 TEST_F(StatePackageTest, SetPropertyValue_Error_Object_MissingProperty) {
   chromeos::ErrorPtr error;
-  ASSERT_TRUE(package_->SetPropertyValue(
-      "direction", *CreateDictionaryValue("{'azimuth': 10.0}"), &error));
   ASSERT_FALSE(package_->SetPropertyValue(
       "direction", *CreateDictionaryValue("{'altitude': 10.0}"), &error));
   EXPECT_EQ(errors::commands::kDomain, error->GetDomain());