shill: Create a Mapped custom property accessor

Create an accessor that passes a property key to the get/set/clear.

BUG=chromium-os:28303
TEST=New unit tests

Change-Id: If9cb94cf60427c49c5e87c828b787fc6d17d1a0c
Reviewed-on: https://gerrit.chromium.org/gerrit/18988
Commit-Ready: Paul Stewart <pstew@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/property_accessor.h b/property_accessor.h
index 39cd611..9005192 100644
--- a/property_accessor.h
+++ b/property_accessor.h
@@ -212,6 +212,58 @@
   DISALLOW_COPY_AND_ASSIGN(CustomWriteOnlyAccessor);
 };
 
+// CustomMappedAccessor<> passes an argument to the getter and setter
+// so that a generic method can be used, for example one that accesses the
+// property in a map.
+template<class C, class T, class A>
+class CustomMappedAccessor : public AccessorInterface<T> {
+ public:
+  // |target| is the object on which to call the methods |getter| and |setter|.
+  // |setter| is allowed to be NULL, in which case we will simply reject
+  // attempts to set via the accessor.
+  // |argument| is passed to the getter and setter methods to disambiguate
+  // between different properties in |target|.
+  // It is an error to pass NULL for any of |target|, |clearer| or |getter|.
+  CustomMappedAccessor(C *target,
+                       void(C::*clearer)(const A &argument, Error *error),
+                       T(C::*getter)(const A &argument, Error *error),
+                       void(C::*setter)(const T &value, const A &argument,
+                                        Error *error),
+                       const A &argument)
+      : target_(target),
+        clearer_(clearer),
+        getter_(getter),
+        setter_(setter),
+        argument_(argument) {
+    DCHECK(clearer);
+    DCHECK(target);
+    DCHECK(getter);
+  }
+  virtual ~CustomMappedAccessor() {}
+
+  void Clear(Error *error) {
+    (target_->*clearer_)(argument_, error);
+  }
+  T Get(Error *error) {
+    return (target_->*getter_)(argument_, error);
+  }
+  void Set(const T &value, Error *error) {
+    if (setter_) {
+      (target_->*setter_)(value, argument_, error);
+    } else {
+      error->Populate(Error::kInvalidArguments, "Property is read-only");
+    }
+  }
+
+ private:
+  C *const target_;
+  void(C::*const clearer_)(const A &argument, Error *error);
+  T(C::*const getter_)(const A &argument, Error *error);
+  void(C::*const setter_)(const T &value, const A &argument, Error *error);
+  A argument_;
+  DISALLOW_COPY_AND_ASSIGN(CustomMappedAccessor);
+};
+
 }  // namespace shill
 
 #endif  // SHILL_PROPERTY_ACCESSOR_
diff --git a/property_accessor_unittest.cc b/property_accessor_unittest.cc
index f95abdf..b52fe19 100644
--- a/property_accessor_unittest.cc
+++ b/property_accessor_unittest.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include <base/basictypes.h>
+#include <base/stl_util.h>
 #include <gtest/gtest.h>
 #include <gmock/gmock.h>
 
@@ -378,4 +379,59 @@
   }
 }
 
+class StringMapWrapper {
+ public:
+  void Clear(const string &key, Error */*error*/) {
+    value_.erase(key);
+  }
+  string Get(const string &key, Error */*error*/) {
+    EXPECT_TRUE(ContainsKey(value_, key));
+    return value_[key];
+  }
+  void Set(const string &value, const string &key, Error */*error*/) {
+    value_[key] = value;
+  }
+
+  map<string,string> value_;
+};
+
+TEST(PropertyAccessorTest, CustomMappedAccessor) {
+  const string kKey = "entry_key";
+  const string kValue = "entry_value";
+  {
+    // Test reading.
+    StringMapWrapper wrapper;
+    CustomMappedAccessor<StringMapWrapper, string, string> accessor(
+        &wrapper, &StringMapWrapper::Clear, &StringMapWrapper::Get,
+        &StringMapWrapper::Set, kKey);
+    wrapper.value_[kKey] = kValue;
+    Error error;
+    EXPECT_EQ(kValue, accessor.Get(&error));
+    EXPECT_TRUE(error.IsSuccess());
+  }
+  {
+    // Test writing.
+    StringMapWrapper wrapper;
+    CustomMappedAccessor<StringMapWrapper, string, string> accessor(
+        &wrapper, &StringMapWrapper::Clear, &StringMapWrapper::Get,
+        &StringMapWrapper::Set, kKey);
+    Error error;
+    accessor.Set(kValue, &error);
+    EXPECT_TRUE(error.IsSuccess());
+    EXPECT_EQ(kValue, wrapper.value_[kKey]);
+  }
+  {
+    // Test clearing.
+    StringMapWrapper wrapper;
+    CustomMappedAccessor<StringMapWrapper, string, string> accessor(
+        &wrapper, &StringMapWrapper::Clear, &StringMapWrapper::Get,
+        &StringMapWrapper::Set, kKey);
+    wrapper.value_[kKey] = kValue;
+    Error error;
+    accessor.Clear(&error);
+    EXPECT_TRUE(error.IsSuccess());
+    EXPECT_FALSE(ContainsKey(wrapper.value_, kKey));
+  }
+}
+
 }  // namespace shill