shill: teach property accessors how to clear properties,
and update code that instantiates property accessors
accordingly.

BUG=chromium-os:24814
TEST=new unit tests

Change-Id: Iae385c331648e74916c2eb2b69c41ccc9cdcafdf
Reviewed-on: https://gerrit.chromium.org/gerrit/15289
Reviewed-by: mukesh agrawal <quiche@chromium.org>
Tested-by: mukesh agrawal <quiche@chromium.org>
Commit-Ready: mukesh agrawal <quiche@chromium.org>
diff --git a/property_accessor.h b/property_accessor.h
index 03fa7c0..39cd611 100644
--- a/property_accessor.h
+++ b/property_accessor.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 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.
 
@@ -15,9 +15,12 @@
 namespace shill {
 
 // Templated implementations of AccessorInterface<>.
-// PropertyAccessor<> and ConstPropertyAccessor<> respectively provide
-// R/W and R/O access to the value pointed to by |property|.
-// this allows a class to easily map strings to member variables, so that
+//
+// PropertyAccessor<>, ConstPropertyAccessor<>, and
+// WriteOnlyPropertyAccessor<> provide R/W, R/O, and W/O access
+// (respectively) to the value pointed to by |property|.
+//
+// This allows a class to easily map strings to member variables, so that
 // pieces of state stored in the class can be queried or updated by name.
 //
 //   bool foo = true;
@@ -27,14 +30,18 @@
 //   accessors["foo"]->Set(false);  // returns true, because setting is allowed.
 //                                  // foo == false, new_foo == true
 //   new_foo = accessors["foo"]->Get();  // new_foo == false
+//   // Clear resets |foo| to its value when the PropertyAccessor was created.
+//   accessors["foo"]->Clear();  // foo == true
 template <class T>
 class PropertyAccessor : public AccessorInterface<T> {
  public:
-  explicit PropertyAccessor(T *property) : property_(property) {
+  explicit PropertyAccessor(T *property)
+      : property_(property), default_value_(*property) {
     DCHECK(property);
   }
   virtual ~PropertyAccessor() {}
 
+  void Clear(Error *error) { Set(default_value_, error); }
   T Get(Error */*error*/) { return *property_; }
   void Set(const T &value, Error */*error*/) {
     *property_ = value;
@@ -42,6 +49,7 @@
 
  private:
   T * const property_;
+  const T default_value_;
   DISALLOW_COPY_AND_ASSIGN(PropertyAccessor);
 };
 
@@ -53,6 +61,11 @@
   }
   virtual ~ConstPropertyAccessor() {}
 
+  void Clear(Error *error) {
+    // TODO(quiche): check if this is the right error.
+    // (maybe Error::kInvalidProperty instead?)
+    error->Populate(Error::kInvalidArguments, "Property is read-only");
+  }
   T Get(Error */*error*/) { return *property_; }
   void Set(const T &/*value*/, Error *error) {
     // TODO(quiche): check if this is the right error.
@@ -68,11 +81,13 @@
 template <class T>
 class WriteOnlyPropertyAccessor : public AccessorInterface<T> {
  public:
-  explicit WriteOnlyPropertyAccessor(T *property) : property_(property) {
+  explicit WriteOnlyPropertyAccessor(T *property)
+      : property_(property), default_value_(*property) {
     DCHECK(property);
   }
   virtual ~WriteOnlyPropertyAccessor() {}
 
+  void Clear(Error *error) { Set(default_value_, error); }
   T Get(Error *error) {
     error->Populate(Error::kPermissionDenied, "Property is write-only");
     return T();
@@ -87,12 +102,16 @@
   FRIEND_TEST(PropertyAccessorTest, StringCorrectness);
 
   T * const property_;
+  const T default_value_;
   DISALLOW_COPY_AND_ASSIGN(WriteOnlyPropertyAccessor);
 };
 
 // CustomAccessor<> allows custom getter and setter methods to be provided.
-// Thus, if the state to be returned is to be derived on-demand, we can
-// still fit it into the AccessorInterface<> framework.
+// Thus, if the state to be returned is to be derived on-demand, or if
+// setting the property requires validation, we can still fit it into the
+// AccessorInterface<> framework.
+//
+// If the property is write-only, use CustomWriteOnlyAccessor instead.
 template<class C, class T>
 class CustomAccessor : public AccessorInterface<T> {
  public:
@@ -104,20 +123,22 @@
                  T(C::*getter)(Error *error),
                  void(C::*setter)(const T &value, Error *error))
       : target_(target),
+        default_value_(),
         getter_(getter),
         setter_(setter) {
     DCHECK(target);
+    DCHECK(getter);  // otherwise, use CustomWriteOnlyAccessor
+    if (setter_) {
+      Error e;
+      default_value_ = Get(&e);
+    }
   }
   virtual ~CustomAccessor() {}
 
+  void Clear(Error *error) { Set(default_value_, error); }
   T Get(Error *error) {
-    if (getter_)
-      return storage_ = (target_->*getter_)(error);
-
-    error->Populate(Error::kPermissionDenied, "Property is write-only");
-    return T();
+    return (target_->*getter_)(error);
   }
-
   void Set(const T &value, Error *error) {
     if (setter_) {
       (target_->*setter_)(value, error);
@@ -127,15 +148,70 @@
   }
 
  private:
-  C * const target_;
-  // Get() returns a const&, so we need to have internal storage to which to
-  // return a reference.
-  T storage_;
-  T(C::*getter_)(Error *error);
-  void(C::*setter_)(const T &value, Error *error);
+  C *const target_;
+  // |default_value_| is non-const because it can't be initialized in
+  // the initializer list.
+  T default_value_;
+  T(C::*const getter_)(Error *error);
+  void(C::*const setter_)(const T &value, Error *error);
   DISALLOW_COPY_AND_ASSIGN(CustomAccessor);
 };
 
+// CustomWriteOnlyAccessor<> allows a custom writer method to be provided.
+// Get returns an error automatically. Clear resets the value to a
+// default value.
+template<class C, class T>
+class CustomWriteOnlyAccessor : public AccessorInterface<T> {
+ public:
+  // |target| is the object on which to call |setter| and |clearer|.
+  //
+  // |target| and |setter| must be non-NULL.
+  //
+  // Either |clearer| or |default_value|, but not both, must be non-NULL.
+  // Whichever is non-NULL is used to clear the property.
+  CustomWriteOnlyAccessor(C *target,
+                          void(C::*setter)(const T &value, Error *error),
+                          void(C::*clearer)(Error *error),
+                          const T *default_value)
+      : target_(target),
+        setter_(setter),
+        clearer_(clearer),
+        default_value_() {
+    DCHECK(target);
+    DCHECK(setter);
+    DCHECK(clearer || default_value);
+    DCHECK(!clearer || !default_value);
+    if (default_value) {
+      default_value_ = *default_value;
+    }
+  }
+  virtual ~CustomWriteOnlyAccessor() {}
+
+  void Clear(Error *error) {
+    if (clearer_) {
+      (target_->*clearer_)(error);
+    } else {
+      Set(default_value_, error);
+    }
+  }
+  T Get(Error *error) {
+    error->Populate(Error::kPermissionDenied, "Property is write-only");
+    return T();
+  }
+  void Set(const T &value, Error *error) {
+    (target_->*setter_)(value, error);
+  }
+
+ private:
+  C *const target_;
+  void(C::*const setter_)(const T &value, Error *error);
+  void(C::*const clearer_)(Error *error);
+  // |default_value_| is non-const because it can't be initialized in
+  // the initializer list.
+  T default_value_;
+  DISALLOW_COPY_AND_ASSIGN(CustomWriteOnlyAccessor);
+};
+
 }  // namespace shill
 
 #endif  // SHILL_PROPERTY_ACCESSOR_